diff --git a/src/apps/structure-info/volume.ts b/src/apps/structure-info/volume.ts
index ef38fcb190d1a90a8855321319ea18063188198c..cfedb46e9e4a7d520469ca3b1206d1992a0d5522 100644
--- a/src/apps/structure-info/volume.ts
+++ b/src/apps/structure-info/volume.ts
@@ -8,38 +8,36 @@ import * as fs from 'fs';
 import * as argparse from 'argparse';
 import * as util from 'util';
 
-import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { downloadCif } from './helpers';
 import { CIF } from '../../mol-io/reader/cif';
-import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
 import { Table } from '../../mol-data/db';
 import { StringBuilder } from '../../mol-util';
 import { Task } from '../../mol-task';
 import { createVolumeIsosurfaceMesh } from '../../mol-repr/volume/isosurface';
 import { Theme } from '../../mol-theme/theme';
-import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
+import { volumeFromDensityServerData, DscifFormat } from '../../mol-model-formats/volume/density-server';
 
 require('util.promisify').shim();
 const writeFileAsync = util.promisify(fs.writeFile);
 
-type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
-
 async function getVolume(url: string): Promise<Volume> {
     const cif = await downloadCif(url, true);
     const data = CIF.schema.densityServer(cif.blocks[1]);
-    return { source: data, volume: await volumeFromDensityServerData(data).run() };
+    return await volumeFromDensityServerData(data).run();
 }
 
-function print(data: Volume) {
-    const { volume_data_3d_info } = data.source;
+function print(volume: Volume) {
+    if (!DscifFormat.is(volume.sourceData)) return;
+    const { volume_data_3d_info } = volume.sourceData.data;
     const row = Table.getRow(volume_data_3d_info, 0);
     console.log(row);
-    if (data.volume.transform) console.log(data.volume.transform);
-    console.log(data.volume.dataStats);
+    console.log(volume.grid.transform);
+    console.log(volume.grid.stats);
 }
 
-async function doMesh(data: Volume, filename: string) {
-    const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, Theme.createEmpty(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
+async function doMesh(volume: Volume, filename: string) {
+    const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) } )).run();
     console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
 
     // Export the mesh in OBJ format.
diff --git a/src/extensions/cellpack/property.ts b/src/extensions/cellpack/property.ts
index 700776fea1689c23bb2d4c3c0b76b28a0b03ee07..56e58cfb26c21b719ac79c5344c0effbfc909c33 100644
--- a/src/extensions/cellpack/property.ts
+++ b/src/extensions/cellpack/property.ts
@@ -5,9 +5,10 @@
  */
 
 import { CustomStructureProperty } from '../../mol-model-props/common/custom-structure-property';
-import { Structure, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { Structure } from '../../mol-model/structure';
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export type CellPackInfoValue = {
     packingsCount: number
diff --git a/src/extensions/pdbe/preferred-assembly.ts b/src/extensions/pdbe/preferred-assembly.ts
index 52a2d3c3218cb7e82f803bce135ed94f1ceb5da7..f4a11f08bdd103c1d969d8f5403653cc85b5198c 100644
--- a/src/extensions/pdbe/preferred-assembly.ts
+++ b/src/extensions/pdbe/preferred-assembly.ts
@@ -7,9 +7,10 @@
 import { Column, Table } from '../../mol-data/db';
 import { toTable } from '../../mol-io/reader/cif/schema';
 import { CifWriter } from '../../mol-io/writer/cif';
-import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { Model } from '../../mol-model/structure';
 import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
 import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export namespace PDBePreferredAssembly {
     export type Property = string
diff --git a/src/extensions/pdbe/struct-ref-domain.ts b/src/extensions/pdbe/struct-ref-domain.ts
index 4b025f55230860d427b7c6be975b672e134d3881..c92a385b4984c10f73ed9e166ccf62a765238294 100644
--- a/src/extensions/pdbe/struct-ref-domain.ts
+++ b/src/extensions/pdbe/struct-ref-domain.ts
@@ -7,9 +7,10 @@
 import { Column, Table } from '../../mol-data/db';
 import { toTable } from '../../mol-io/reader/cif/schema';
 import { CifWriter } from '../../mol-io/writer/cif';
-import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { Model } from '../../mol-model/structure';
 import { PropertyWrapper } from '../../mol-model-props/common/wrapper';
 import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export namespace PDBeStructRefDomain {
     export type Property = PropertyWrapper<Table<Schema['pdbe_struct_ref_domain']> | undefined>
diff --git a/src/extensions/pdbe/structure-quality-report/prop.ts b/src/extensions/pdbe/structure-quality-report/prop.ts
index e51f875867f89598a96b3e1dd063f0429c36b1ea..e1342d51b9abfe07a347bd4afcb9258b04ba0ac0 100644
--- a/src/extensions/pdbe/structure-quality-report/prop.ts
+++ b/src/extensions/pdbe/structure-quality-report/prop.ts
@@ -9,7 +9,7 @@ import { Column, Table } from '../../../mol-data/db';
 import { toTable } from '../../../mol-io/reader/cif/schema';
 import { mmCIF_residueId_schema } from '../../../mol-io/reader/cif/schema/mmcif-extras';
 import { CifWriter } from '../../../mol-io/writer/cif';
-import { Model, CustomPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from '../../../mol-model/structure';
+import { Model, ResidueIndex, Unit, IndexedCustomProperty } from '../../../mol-model/structure';
 import { residueIdFields } from '../../../mol-model/structure/export/categories/atom_site';
 import { StructureElement, CifExportContext, Structure } from '../../../mol-model/structure/structure';
 import { CustomPropSymbol } from '../../../mol-script/language/symbol';
@@ -22,6 +22,7 @@ import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
 import { CustomProperty } from '../../../mol-model-props/common/custom-property';
 import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
 import { Asset } from '../../../mol-util/assets';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export { StructureQualityReport };
 
diff --git a/src/extensions/rcsb/assembly-symmetry/prop.ts b/src/extensions/rcsb/assembly-symmetry/prop.ts
index 7bd1286448a08726257d2a3604adea9b8cc6f43f..c88daa19ab72297f9f86685d8263a7187beb2eed 100644
--- a/src/extensions/rcsb/assembly-symmetry/prop.ts
+++ b/src/extensions/rcsb/assembly-symmetry/prop.ts
@@ -8,7 +8,7 @@ import { AssemblySymmetryQuery, AssemblySymmetryQueryVariables } from '../graphq
 import query from '../graphql/symmetry.gql';
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { CustomPropertyDescriptor, Structure, Model, StructureSelection, QueryContext } from '../../../mol-model/structure';
+import { Structure, Model, StructureSelection, QueryContext } from '../../../mol-model/structure';
 import { Database as _Database, Column } from '../../../mol-data/db';
 import { GraphQLClient } from '../../../mol-util/graphql-client';
 import { CustomProperty } from '../../../mol-model-props/common/custom-property';
@@ -19,6 +19,7 @@ import { ReadonlyVec3 } from '../../../mol-math/linear-algebra/3d/vec3';
 import { SetUtils } from '../../../mol-util/set';
 import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
 import { compile } from '../../../mol-script/runtime/query/compiler';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 const BiologicalAssemblyNames = new Set([
     'author_and_software_defined_assembly',
diff --git a/src/extensions/rcsb/validation-report/prop.ts b/src/extensions/rcsb/validation-report/prop.ts
index a17bc6fd1feeab3b336e8057bbc93af2a1f8f825..aec4ea9a2956f4f6e1d3fa84129c788faeb1a840 100644
--- a/src/extensions/rcsb/validation-report/prop.ts
+++ b/src/extensions/rcsb/validation-report/prop.ts
@@ -5,7 +5,7 @@
  */
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { CustomPropertyDescriptor, Structure, Unit } from '../../../mol-model/structure';
+import { Structure, Unit } from '../../../mol-model/structure';
 import { CustomProperty } from '../../../mol-model-props/common/custom-property';
 import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
 import { Model, ElementIndex, ResidueIndex } from '../../../mol-model/structure/model';
@@ -21,6 +21,7 @@ import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
 import { CustomPropSymbol } from '../../../mol-script/language/symbol';
 import Type from '../../../mol-script/language/type';
 import { Asset } from '../../../mol-util/assets';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export { ValidationReport };
 
diff --git a/src/mol-model-formats/structure/basic/parser.ts b/src/mol-model-formats/structure/basic/parser.ts
index bf7129e2a08e21145a50020e227c1f8e6aac6ee2..8773e4e2a7d776c3f39e5630fb443942d8434424 100644
--- a/src/mol-model-formats/structure/basic/parser.ts
+++ b/src/mol-model-formats/structure/basic/parser.ts
@@ -10,7 +10,7 @@ import { RuntimeContext } from '../../../mol-task';
 import UUID from '../../../mol-util/uuid';
 import { Model } from '../../../mol-model/structure/model/model';
 import { Entities } from '../../../mol-model/structure/model/properties/common';
-import { CustomProperties } from '../../../mol-model/structure';
+import { CustomProperties } from '../../../mol-model/custom-property';
 import { getAtomicHierarchyAndConformation } from './atomic';
 import { getCoarse, EmptyCoarse, CoarseData } from './coarse';
 import { getSequence } from './sequence';
diff --git a/src/mol-model-formats/structure/common/property.ts b/src/mol-model-formats/structure/common/property.ts
index cbc1718f8a99beb821d9f81e0b947f05f982341a..63709daac1bbcfe8fb01c35602cc4fcc39fb09ae 100644
--- a/src/mol-model-formats/structure/common/property.ts
+++ b/src/mol-model-formats/structure/common/property.ts
@@ -4,8 +4,9 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Model } from '../../../mol-model/structure';
+import { Model } from '../../../mol-model/structure';
 import { ModelFormat } from '../../format';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 class FormatRegistry<T> {
     private map = new Map<ModelFormat['kind'], (model: Model) => T | undefined>()
diff --git a/src/mol-model-formats/structure/property/anisotropic.ts b/src/mol-model-formats/structure/property/anisotropic.ts
index 16cb3591e646ef9084ae255d4deeecc989bf3e5e..f6cc407d15d2c51ffd9319b82755f0fc50a2e0e6 100644
--- a/src/mol-model-formats/structure/property/anisotropic.ts
+++ b/src/mol-model-formats/structure/property/anisotropic.ts
@@ -5,7 +5,7 @@
  */
 
 import { Table, Column } from '../../../mol-data/db';
-import { CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
 import { CifWriter } from '../../../mol-io/writer/cif';
 import { FormatPropertyProvider } from '../common/property';
diff --git a/src/mol-model-formats/structure/property/bonds/comp.ts b/src/mol-model-formats/structure/property/bonds/comp.ts
index ed510b10fa01f68e7efaf73a43e6720ef16dd331..b30e9dac92f4e5c62ec908ca22af7dc5a3485d3b 100644
--- a/src/mol-model-formats/structure/property/bonds/comp.ts
+++ b/src/mol-model-formats/structure/property/bonds/comp.ts
@@ -7,7 +7,7 @@
 
 import { Model } from '../../../../mol-model/structure/model/model';
 import { BondType } from '../../../../mol-model/structure/model/types';
-import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
 import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
 import { CifWriter } from '../../../../mol-io/writer/cif';
 import { Table } from '../../../../mol-data/db';
diff --git a/src/mol-model-formats/structure/property/bonds/index-pair.ts b/src/mol-model-formats/structure/property/bonds/index-pair.ts
index 7a09535eb37b4026a4055d0c7b7c7a91080528e1..485fe8d737818499cc809bdf4c5eb8a957da7dca 100644
--- a/src/mol-model-formats/structure/property/bonds/index-pair.ts
+++ b/src/mol-model-formats/structure/property/bonds/index-pair.ts
@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
 import { IntAdjacencyGraph } from '../../../../mol-math/graph';
 import { Column } from '../../../../mol-data/db';
 import { FormatPropertyProvider } from '../../common/property';
diff --git a/src/mol-model-formats/structure/property/bonds/struct_conn.ts b/src/mol-model-formats/structure/property/bonds/struct_conn.ts
index 494ac4d2b6c0c62c28847de18753189b9730bbcf..ae48045cdadc8a6a6f9f4c1bf3708857e6d3697c 100644
--- a/src/mol-model-formats/structure/property/bonds/struct_conn.ts
+++ b/src/mol-model-formats/structure/property/bonds/struct_conn.ts
@@ -9,7 +9,7 @@ import { Model } from '../../../../mol-model/structure/model/model';
 import { Structure } from '../../../../mol-model/structure';
 import { BondType } from '../../../../mol-model/structure/model/types';
 import { Column, Table } from '../../../../mol-data/db';
-import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
 import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
 import { SortedArray } from '../../../../mol-data/int';
 import { CifWriter } from '../../../../mol-io/writer/cif';
diff --git a/src/mol-model-formats/structure/property/secondary-structure.ts b/src/mol-model-formats/structure/property/secondary-structure.ts
index e79e9a321900da33f4def2872497d8a7b0d2b9f0..616cd18bf2a6fbbf5eabe1a7475b6f1914537467 100644
--- a/src/mol-model-formats/structure/property/secondary-structure.ts
+++ b/src/mol-model-formats/structure/property/secondary-structure.ts
@@ -13,7 +13,7 @@ import { SecondaryStructure } from '../../../mol-model/structure/model/propertie
 import { Column, Table } from '../../../mol-data/db';
 import { ChainIndex, ResidueIndex } from '../../../mol-model/structure/model/indexing';
 import { FormatPropertyProvider } from '../common/property';
-import { CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export { ModelSecondaryStructure };
 
diff --git a/src/mol-model-formats/structure/property/symmetry.ts b/src/mol-model-formats/structure/property/symmetry.ts
index 75330f6f657469b770f2345054864d98642d2f65..22f610e86430653c9cca30c4fc34305ee1b62e7a 100644
--- a/src/mol-model-formats/structure/property/symmetry.ts
+++ b/src/mol-model-formats/structure/property/symmetry.ts
@@ -10,7 +10,7 @@ import { Spacegroup, SpacegroupCell, SymmetryOperator } from '../../../mol-math/
 import { Tensor, Vec3, Mat3 } from '../../../mol-math/linear-algebra';
 import { Symmetry } from '../../../mol-model/structure/model/properties/symmetry';
 import { createAssemblies } from './assembly';
-import { CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 import { FormatPropertyProvider } from '../common/property';
 import { Table } from '../../../mol-data/db';
 
diff --git a/src/mol-model-formats/volume/ccp4.ts b/src/mol-model-formats/volume/ccp4.ts
index 8ccf5ad7aa02d9261492d427211a96a534a8a489..2a05fce5e6762e56abaa1fa7c446ff80f70325c3 100644
--- a/src/mol-model-formats/volume/ccp4.ts
+++ b/src/mol-model-formats/volume/ccp4.ts
@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
 import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
@@ -14,6 +14,7 @@ import { getCcp4ValueType } from '../../mol-io/reader/ccp4/parser';
 import { TypedArrayValueType } from '../../mol-io/common/typed-array';
 import { arrayMin, arrayRms, arrayMean, arrayMax } from '../../mol-util/array';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
 /** When available (e.g. in MRC files) use ORIGIN records instead of N[CRS]START */
 export function getCcp4Origin(header: Ccp4Header): Vec3 {
@@ -39,8 +40,8 @@ function getTypedArrayCtor(header: Ccp4Header) {
     throw Error(`${valueType} is not a supported value format.`);
 }
 
-export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, offset?: Vec3, label?: string }): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async ctx => {
+export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, offset?: Vec3, label?: string }): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async ctx => {
         const { header, values } = source;
         const size = Vec3.create(header.xLength, header.yLength, header.zLength);
         if (params && params.voxelSize) Vec3.mul(size, size, params.voxelSize);
@@ -69,15 +70,19 @@ export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, of
 
         return {
             label: params?.label,
-            transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
-            data,
-            dataStats: {
-                min: isNaN(header.AMIN) ? arrayMin(values) : header.AMIN,
-                max: isNaN(header.AMAX) ? arrayMax(values) : header.AMAX,
-                mean: isNaN(header.AMEAN) ? arrayMean(values) : header.AMEAN,
-                sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
+            grid: {
+                transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
+                cells: data,
+                stats: {
+                    min: isNaN(header.AMIN) ? arrayMin(values) : header.AMIN,
+                    max: isNaN(header.AMAX) ? arrayMax(values) : header.AMAX,
+                    mean: isNaN(header.AMEAN) ? arrayMean(values) : header.AMEAN,
+                    sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
+                },
             },
-            sourceData: Ccp4Format.create(source)
+            sourceData: Ccp4Format.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }
diff --git a/src/mol-model-formats/volume/cube.ts b/src/mol-model-formats/volume/cube.ts
index 416944d77a6e44ff6ef4d399b5f5fd23ef672f58..4db35bee33106fb05e7f2eabc1bc7e83b3b65056 100644
--- a/src/mol-model-formats/volume/cube.ts
+++ b/src/mol-model-formats/volume/cube.ts
@@ -6,13 +6,14 @@
 
 import { CubeFile } from '../../mol-io/reader/cube/parser';
 import { Mat4, Tensor } from '../../mol-math/linear-algebra';
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
-export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string }): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async () => {
+export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string }): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async () => {
         const { header, values: sourceValues } = source;
         const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
 
@@ -45,15 +46,19 @@ export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number,
 
         return {
             label: params?.label,
-            transform: { kind: 'matrix', matrix },
-            data,
-            dataStats: {
-                min: arrayMin(values),
-                max: arrayMax(values),
-                mean: arrayMean(values),
-                sigma: arrayRms(values)
+            grid: {
+                transform: { kind: 'matrix', matrix },
+                cells: data,
+                stats: {
+                    min: arrayMin(values),
+                    max: arrayMax(values),
+                    mean: arrayMean(values),
+                    sigma: arrayRms(values)
+                },
             },
-            sourceData: CubeFormat.create(source)
+            sourceData: CubeFormat.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }
diff --git a/src/mol-model-formats/volume/density-server.ts b/src/mol-model-formats/volume/density-server.ts
index bb35f01408b9032a3c4bf3c8cf96fe8bdd809809..ad2a5ccd649f480d375cb88538cc937e72bfce30 100644
--- a/src/mol-model-formats/volume/density-server.ts
+++ b/src/mol-model-formats/volume/density-server.ts
@@ -5,14 +5,15 @@
  */
 
 import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
 import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
-export function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async ctx => {
+export function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async ctx => {
         const { volume_data_3d_info: info, volume_data_3d: values } = source;
         const cell = SpacegroupCell.create(
             info.spacegroup_number.value(0),
@@ -35,15 +36,19 @@ export function volumeFromDensityServerData(source: DensityServer_Data_Database)
         const dimensions = Vec3.ofArray(normalizeOrder(info.dimensions.value(0)));
 
         return {
-            transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
-            data,
-            dataStats: {
-                min: info.min_sampled.value(0),
-                max: info.max_sampled.value(0),
-                mean: info.mean_sampled.value(0),
-                sigma: info.sigma_sampled.value(0)
+            grid: {
+                transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
+                cells: data,
+                stats: {
+                    min: info.min_sampled.value(0),
+                    max: info.max_sampled.value(0),
+                    mean: info.mean_sampled.value(0),
+                    sigma: info.sigma_sampled.value(0)
+                },
             },
-            sourceData: DscifFormat.create(source)
+            sourceData: DscifFormat.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }
diff --git a/src/mol-model-formats/volume/dsn6.ts b/src/mol-model-formats/volume/dsn6.ts
index f6ef76cd6da544664d58060905409f22b83a47f4..b49ed46f10e2a1c0011225373d3b2d4ff464a19d 100644
--- a/src/mol-model-formats/volume/dsn6.ts
+++ b/src/mol-model-formats/volume/dsn6.ts
@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
 import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
@@ -12,9 +12,10 @@ import { degToRad } from '../../mol-math/misc';
 import { Dsn6File } from '../../mol-io/reader/dsn6/schema';
 import { arrayMin, arrayMax, arrayMean, arrayRms } from '../../mol-util/array';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
-export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async ctx => {
+export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async ctx => {
         const { header, values } = source;
         const size = Vec3.create(header.xlen, header.ylen, header.zlen);
         if (params && params.voxelSize) Vec3.mul(size, size, params.voxelSize);
@@ -34,15 +35,19 @@ export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, la
 
         return {
             label: params?.label,
-            transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
-            data,
-            dataStats: {
-                min: arrayMin(values),
-                max: arrayMax(values),
-                mean: arrayMean(values),
-                sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
+            grid: {
+                transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
+                cells: data,
+                stats: {
+                    min: arrayMin(values),
+                    max: arrayMax(values),
+                    mean: arrayMean(values),
+                    sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
+                },
             },
-            sourceData: Dsn6Format.create(source)
+            sourceData: Dsn6Format.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }
diff --git a/src/mol-model-formats/volume/dx.ts b/src/mol-model-formats/volume/dx.ts
index 2ec9007ca7b601639f74db0d8486f65816c524ef..08424a9232d83211778827a6942c0064560ce916 100644
--- a/src/mol-model-formats/volume/dx.ts
+++ b/src/mol-model-formats/volume/dx.ts
@@ -6,13 +6,14 @@
 
 import { DxFile } from '../../mol-io/reader/dx/parser';
 import { Mat4, Tensor } from '../../mol-math/linear-algebra';
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
-export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async () => {
+export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async () => {
         const { header, values } = source;
         const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
         const data = Tensor.create(space, Tensor.Data1(values));
@@ -22,15 +23,19 @@ export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<
 
         return {
             label: params?.label,
-            transform: { kind: 'matrix', matrix },
-            data,
-            dataStats: {
-                min: arrayMin(values),
-                max: arrayMax(values),
-                mean: arrayMean(values),
-                sigma: arrayRms(values)
+            grid: {
+                transform: { kind: 'matrix', matrix },
+                cells: data,
+                stats: {
+                    min: arrayMin(values),
+                    max: arrayMax(values),
+                    mean: arrayMean(values),
+                    sigma: arrayRms(values)
+                },
             },
-            sourceData: DxFormat.create(source)
+            sourceData: DxFormat.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }
diff --git a/src/mol-model-props/common/custom-element-property.ts b/src/mol-model-props/common/custom-element-property.ts
index a54b47798f5b1773653c19d9da4e8ba588d20eb8..afd22d801489a3736e89fb447052c17947750ded 100644
--- a/src/mol-model-props/common/custom-element-property.ts
+++ b/src/mol-model-props/common/custom-element-property.ts
@@ -5,7 +5,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ElementIndex, Model, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { ElementIndex, Model } from '../../mol-model/structure';
 import { StructureElement } from '../../mol-model/structure/structure';
 import { Location } from '../../mol-model/location';
 import { ThemeDataContext } from '../../mol-theme/theme';
@@ -16,6 +16,7 @@ import { OrderedSet } from '../../mol-data/int';
 import { CustomModelProperty } from './custom-model-property';
 import { CustomProperty } from './custom-property';
 import { LociLabelProvider } from '../../mol-plugin-state/manager/loci-label';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export { CustomElementProperty };
 
diff --git a/src/mol-model-props/common/custom-model-property.ts b/src/mol-model-props/common/custom-model-property.ts
index 2e8f14f4c99e5369659b4a5cc9318259c3366e8c..75ce00d8f16a4179e830507728aa5935577b5017 100644
--- a/src/mol-model-props/common/custom-model-property.ts
+++ b/src/mol-model-props/common/custom-model-property.ts
@@ -4,10 +4,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Model } from '../../mol-model/structure';
+import { Model } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ValueBox } from '../../mol-util';
 import { CustomProperty } from './custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export { CustomModelProperty };
 
diff --git a/src/mol-model-props/common/custom-property.ts b/src/mol-model-props/common/custom-property.ts
index 5aafd392f155b620a318513e7777eb3ba869f042..22fdaf35310f3811aca8830a3114fc12f66a446a 100644
--- a/src/mol-model-props/common/custom-property.ts
+++ b/src/mol-model-props/common/custom-property.ts
@@ -5,7 +5,7 @@
  */
 
 import { RuntimeContext } from '../../mol-task';
-import { CustomPropertyDescriptor } from '../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ValueBox } from '../../mol-util';
 import { OrderedMap } from 'immutable';
diff --git a/src/mol-model-props/common/custom-structure-property.ts b/src/mol-model-props/common/custom-structure-property.ts
index fefa5f174fe1fe826aa2323ed62668323af76788..8f7a758b5a55ecb129a334b60f7aebb77ed4d569 100644
--- a/src/mol-model-props/common/custom-structure-property.ts
+++ b/src/mol-model-props/common/custom-structure-property.ts
@@ -4,10 +4,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
+import { Structure } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ValueBox } from '../../mol-util';
 import { CustomProperty } from './custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export { CustomStructureProperty };
 
diff --git a/src/mol-model-props/computed/accessible-surface-area.ts b/src/mol-model-props/computed/accessible-surface-area.ts
index a12020eb6128a0bcd40cd260b1018c5593a60753..009754df87eaf2c956712d7d49757c02a5b93d16 100644
--- a/src/mol-model-props/computed/accessible-surface-area.ts
+++ b/src/mol-model-props/computed/accessible-surface-area.ts
@@ -7,12 +7,13 @@
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ShrakeRupleyComputationParams, AccessibleSurfaceArea } from './accessible-surface-area/shrake-rupley';
-import { Structure, CustomPropertyDescriptor, Unit } from '../../mol-model/structure';
+import { Structure, Unit } from '../../mol-model/structure';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
 import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
 import { CustomPropSymbol } from '../../mol-script/language/symbol';
 import Type from '../../mol-script/language/type';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export const AccessibleSurfaceAreaParams = {
     ...ShrakeRupleyComputationParams
diff --git a/src/mol-model-props/computed/interactions.ts b/src/mol-model-props/computed/interactions.ts
index bba29c192a3b0b46046c3b0102024df6afddc26a..e299e9f9692be384874a0ae63e85ef999b5cd9ba 100644
--- a/src/mol-model-props/computed/interactions.ts
+++ b/src/mol-model-props/computed/interactions.ts
@@ -4,11 +4,12 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
+import { Structure } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { computeInteractions, Interactions, InteractionsParams as _InteractionsParams } from './interactions/interactions';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export const InteractionsParams = {
     ..._InteractionsParams
diff --git a/src/mol-model-props/computed/secondary-structure.ts b/src/mol-model-props/computed/secondary-structure.ts
index 383703dfb011bab15855f8824a2cd9728d8997c6..68b790452b9eebdb9311d9a74598af90266ec6ed 100644
--- a/src/mol-model-props/computed/secondary-structure.ts
+++ b/src/mol-model-props/computed/secondary-structure.ts
@@ -12,7 +12,7 @@ import { Unit } from '../../mol-model/structure/structure';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
 import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
-import { CustomPropertyDescriptor } from '../../mol-model/structure/common/custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 import { Model } from '../../mol-model/structure/model';
 
 function getSecondaryStructureParams(data?: Structure) {
diff --git a/src/mol-model-props/computed/valence-model.ts b/src/mol-model-props/computed/valence-model.ts
index 8fa1d24ba17bb44fa3ca30e39d4b6aa70fc638ec..b1fb1959fd25949d4458e01297d58e285ef2e293 100644
--- a/src/mol-model-props/computed/valence-model.ts
+++ b/src/mol-model-props/computed/valence-model.ts
@@ -4,11 +4,12 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
+import { Structure } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { calcValenceModel, ValenceModel, ValenceModelParams as _ValenceModelParams } from './chemistry/valence-model';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export const ValenceModelParams = {
     ..._ValenceModelParams
diff --git a/src/mol-model-props/integrative/cross-link-restraint/format.ts b/src/mol-model-props/integrative/cross-link-restraint/format.ts
index b4e2704e3323abd4f22744d14fb5e3ae1f9eba08..3d65c86b6f1cff33ee6606b9f5995a574b148847 100644
--- a/src/mol-model-props/integrative/cross-link-restraint/format.ts
+++ b/src/mol-model-props/integrative/cross-link-restraint/format.ts
@@ -7,9 +7,10 @@
 import { Model } from '../../../mol-model/structure/model/model';
 import { Table } from '../../../mol-data/db';
 import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
-import { Unit, CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { Unit } from '../../../mol-model/structure';
 import { ElementIndex } from '../../../mol-model/structure/model/indexing';
 import { FormatPropertyProvider } from '../../../mol-model-formats/structure/common/property';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export { ModelCrossLinkRestraint };
 
diff --git a/src/mol-model-props/integrative/cross-link-restraint/property.ts b/src/mol-model-props/integrative/cross-link-restraint/property.ts
index 8b7f07ee495dda32725b3f724c4b51295cded89a..1b2059173832cdb448fbf30be45d67558a62a09b 100644
--- a/src/mol-model-props/integrative/cross-link-restraint/property.ts
+++ b/src/mol-model-props/integrative/cross-link-restraint/property.ts
@@ -5,7 +5,7 @@
  */
 
 import { ModelCrossLinkRestraint } from './format';
-import { Unit, StructureElement, Structure, CustomPropertyDescriptor, Bond} from '../../../mol-model/structure';
+import { Unit, StructureElement, Structure, Bond} from '../../../mol-model/structure';
 import { PairRestraints, PairRestraint } from '../pair-restraints';
 import { CustomStructureProperty } from '../../common/custom-structure-property';
 import { CustomProperty } from '../../common/custom-property';
@@ -15,6 +15,7 @@ import { Sphere3D } from '../../../mol-math/geometry';
 import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
 import { bondLabel } from '../../../mol-theme/label';
 import { Vec3 } from '../../../mol-math/linear-algebra';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export type CrossLinkRestraintValue = PairRestraints<CrossLinkRestraint>
 
diff --git a/src/mol-model/structure/common/custom-property.ts b/src/mol-model/custom-property.ts
similarity index 90%
rename from src/mol-model/structure/common/custom-property.ts
rename to src/mol-model/custom-property.ts
index 3c6723d768a82570d86941c5135ab6519c134c90..0bf52fa2cfc7a015742774d7cd857562ae19dee2 100644
--- a/src/mol-model/structure/common/custom-property.ts
+++ b/src/mol-model/custom-property.ts
@@ -5,11 +5,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CifWriter } from '../../../mol-io/writer/cif';
-import { CifExportContext } from '../export/mmcif';
-import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
-import { UUID } from '../../../mol-util';
-import { Asset } from '../../../mol-util/assets';
+import { CifWriter } from '../mol-io/writer/cif';
+import { CifExportContext } from './structure/export/mmcif';
+import { QuerySymbolRuntime } from '../mol-script/runtime/query/compiler';
+import { UUID } from '../mol-util';
+import { Asset } from '../mol-util/assets';
 
 export { CustomPropertyDescriptor, CustomProperties };
 
diff --git a/src/mol-model/structure.ts b/src/mol-model/structure.ts
index 1069f684fafcdee5f86a16dc8877d2f8d3350083..d8ab4a1a8c79065037c93708cc2864720beda46d 100644
--- a/src/mol-model/structure.ts
+++ b/src/mol-model/structure.ts
@@ -9,5 +9,4 @@ export * from './structure/coordinates';
 export * from './structure/topology';
 export * from './structure/model';
 export * from './structure/structure';
-export * from './structure/query';
-export * from './structure/common/custom-property';
\ No newline at end of file
+export * from './structure/query';
\ No newline at end of file
diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts
index cbbbb2582947178d4cc2b1ae6fe44523b35ae98c..fffe06a4c05e0e8df131e053669fc36779f0424b 100644
--- a/src/mol-model/structure/export/mmcif.ts
+++ b/src/mol-model/structure/export/mmcif.ts
@@ -15,7 +15,7 @@ import { _chem_comp, _pdbx_chem_comp_identifier, _pdbx_nonpoly_scheme } from './
 import { Model } from '../model';
 import { getUniqueEntityIndicesFromStructures, copy_mmCif_category, copy_source_mmCifCategory } from './categories/utils';
 import { _struct_asym, _entity_poly, _entity_poly_seq } from './categories/sequence';
-import { CustomPropertyDescriptor } from '../common/custom-property';
+import { CustomPropertyDescriptor } from '../../custom-property';
 import { atom_site_operator_mapping } from './categories/atom_site_operator_mapping';
 import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
 
diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts
index 00e90d1c9c18ee29abea6000147e029a2054d306..8fe22966d28ef7ad0659a584dd3b254979051109 100644
--- a/src/mol-model/structure/model/model.ts
+++ b/src/mol-model/structure/model/model.ts
@@ -10,7 +10,7 @@ import StructureSequence from './properties/sequence';
 import { AtomicHierarchy, AtomicConformation, AtomicRanges } from './properties/atomic';
 import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
 import { Entities, ChemicalComponentMap, MissingResidues, StructAsymMap } from './properties/common';
-import { CustomProperties } from '../common/custom-property';
+import { CustomProperties } from '../../custom-property';
 import { SaccharideComponentMap } from '../structure/carbohydrates/constants';
 import { ModelFormat } from '../../../mol-model-formats/format';
 import { calcModelCenter } from './util';
diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts
index 05bee252c46352762c0364f5b24ed6098ae57e86..4e40ad996444d318bb3bc051ab59b6207d13053e 100644
--- a/src/mol-model/structure/structure/structure.ts
+++ b/src/mol-model/structure/structure/structure.ts
@@ -25,7 +25,7 @@ import { Vec3, Mat4 } from '../../../mol-math/linear-algebra';
 import { idFactory } from '../../../mol-util/id-factory';
 import { GridLookup3D } from '../../../mol-math/geometry';
 import { UUID } from '../../../mol-util';
-import { CustomProperties } from '../common/custom-property';
+import { CustomProperties } from '../../custom-property';
 import { AtomicHierarchy } from '../model/properties/atomic';
 import { StructureSelection } from '../query/selection';
 import { getBoundary } from '../../../mol-math/geometry/boundary';
diff --git a/src/mol-model/volume.ts b/src/mol-model/volume.ts
index 70aad4db3d39bd24f02fb1cede4b3ec389d69824..f566b4be1baa69113884780c3b18758ab4289b74 100644
--- a/src/mol-model/volume.ts
+++ b/src/mol-model/volume.ts
@@ -1,7 +1,9 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-export * from './volume/data';
\ No newline at end of file
+export * from './volume/volume';
+export * from './volume/grid';
\ No newline at end of file
diff --git a/src/mol-model/volume/data.ts b/src/mol-model/volume/data.ts
deleted file mode 100644
index 2ab5681126a681ba73062b93fed4b485c2695fb1..0000000000000000000000000000000000000000
--- a/src/mol-model/volume/data.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
-import { Tensor, Mat4, Vec3 } from '../../mol-math/linear-algebra';
-import { equalEps } from '../../mol-math/linear-algebra/3d/common';
-import { ModelFormat } from '../../mol-model-formats/format';
-
-/** The basic unit cell that contains the data. */
-interface VolumeDataBase {
-    readonly label?: string,
-    readonly transform: { kind: 'spacegroup', cell: SpacegroupCell, fractionalBox: Box3D } | { kind: 'matrix', matrix: Mat4 },
-    readonly data: Tensor,
-    readonly dataStats: Readonly<{
-        min: number,
-        max: number,
-        mean: number,
-        sigma: number
-    }>
-    readonly sourceData: ModelFormat,
-}
-
-interface VolumeData extends VolumeDataBase {
-    readonly colorVolume?: VolumeDataBase
-}
-
-namespace VolumeData {
-    export const One: VolumeData = {
-        transform: { kind: 'matrix', matrix: Mat4.identity() },
-        data: Tensor.create(Tensor.Space([1, 1, 1], [0, 1, 2]), Tensor.Data1([0])),
-        dataStats: { min: 0, max: 0, mean: 0, sigma: 0 },
-        sourceData: { kind: '', data: '', name: '' }
-    };
-
-    const _scale = Mat4.zero(), _translate = Mat4.zero();
-    export function getGridToCartesianTransform(volume: VolumeData) {
-        if (volume.transform.kind === 'matrix') {
-            return Mat4.copy(Mat4(), volume.transform.matrix);
-        }
-
-        if (volume.transform.kind === 'spacegroup') {
-            const { data: { space } } = volume;
-            const scale = Mat4.fromScaling(_scale, Vec3.div(Vec3.zero(), Box3D.size(Vec3.zero(), volume.transform.fractionalBox), Vec3.ofArray(space.dimensions)));
-            const translate = Mat4.fromTranslation(_translate, volume.transform.fractionalBox.min);
-            return Mat4.mul3(Mat4.zero(), volume.transform.cell.fromFractional, translate, scale);
-        }
-
-        return Mat4.identity();
-    }
-
-    export function areEquivalent(volA: VolumeData, volB: VolumeData) {
-        return volA === volB;
-    }
-}
-
-type VolumeIsoValue = VolumeIsoValue.Absolute | VolumeIsoValue.Relative
-
-namespace VolumeIsoValue {
-    export type Relative = Readonly<{ kind: 'relative', relativeValue: number }>
-    export type Absolute = Readonly<{ kind: 'absolute', absoluteValue: number }>
-
-    export function areSame(a: VolumeIsoValue, b: VolumeIsoValue, stats: VolumeData['dataStats']) {
-        return equalEps(toAbsolute(a, stats).absoluteValue, toAbsolute(b, stats).absoluteValue, stats.sigma / 100);
-    }
-
-    export function absolute(value: number): Absolute { return { kind: 'absolute', absoluteValue: value }; }
-    export function relative(value: number): Relative { return { kind: 'relative', relativeValue: value }; }
-
-    export function calcAbsolute(stats: VolumeData['dataStats'], relativeValue: number): number {
-        return relativeValue * stats.sigma + stats.mean;
-    }
-
-    export function calcRelative(stats: VolumeData['dataStats'], absoluteValue: number): number {
-        return stats.sigma === 0 ? 0 : ((absoluteValue - stats.mean) / stats.sigma);
-    }
-
-    export function toAbsolute(value: VolumeIsoValue, stats: VolumeData['dataStats']): Absolute {
-        return value.kind === 'absolute' ? value : { kind: 'absolute', absoluteValue: VolumeIsoValue.calcAbsolute(stats, value.relativeValue) };
-    }
-
-    export function toRelative(value: VolumeIsoValue, stats: VolumeData['dataStats']): Relative {
-        return value.kind === 'relative' ? value : { kind: 'relative', relativeValue: VolumeIsoValue.calcRelative(stats, value.absoluteValue) };
-    }
-
-    export function toString(value: VolumeIsoValue) {
-        return value.kind === 'relative'
-            ? `${value.relativeValue.toFixed(2)} σ`
-            : `${value.absoluteValue.toPrecision(4)}`;
-    }
-}
-
-export { VolumeData, VolumeIsoValue };
\ No newline at end of file
diff --git a/src/mol-model/volume/grid.ts b/src/mol-model/volume/grid.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53ec19a96f216f04087c77b9a3b76d3995f72deb
--- /dev/null
+++ b/src/mol-model/volume/grid.ts
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
+import { Tensor, Mat4, Vec3 } from '../../mol-math/linear-algebra';
+
+/** The basic unit cell that contains the grid data. */
+interface Grid {
+    readonly transform: Grid.Transform,
+    readonly cells: Tensor,
+    readonly stats: Readonly<{
+        min: number,
+        max: number,
+        mean: number,
+        sigma: number
+    }>
+}
+
+namespace Grid {
+    export const One: Grid = {
+        transform: { kind: 'matrix', matrix: Mat4.identity() },
+        cells: Tensor.create(Tensor.Space([1, 1, 1], [0, 1, 2]), Tensor.Data1([0])),
+        stats: { min: 0, max: 0, mean: 0, sigma: 0 },
+    };
+
+    export type Transform = { kind: 'spacegroup', cell: SpacegroupCell, fractionalBox: Box3D } | { kind: 'matrix', matrix: Mat4 }
+
+    const _scale = Mat4.zero(), _translate = Mat4.zero();
+    export function getGridToCartesianTransform(grid: Grid) {
+        if (grid.transform.kind === 'matrix') {
+            return Mat4.copy(Mat4(), grid.transform.matrix);
+        }
+
+        if (grid.transform.kind === 'spacegroup') {
+            const { cells: { space } } = grid;
+            const scale = Mat4.fromScaling(_scale, Vec3.div(Vec3.zero(), Box3D.size(Vec3.zero(), grid.transform.fractionalBox), Vec3.ofArray(space.dimensions)));
+            const translate = Mat4.fromTranslation(_translate, grid.transform.fractionalBox.min);
+            return Mat4.mul3(Mat4.zero(), grid.transform.cell.fromFractional, translate, scale);
+        }
+
+        return Mat4.identity();
+    }
+
+    export function areEquivalent(gridA: Grid, gridB: Grid) {
+        return gridA === gridB;
+    }
+
+    export function isEmpty(grid: Grid) {
+        return grid.cells.data.length === 0;
+    }
+}
+
+export { Grid };
\ No newline at end of file
diff --git a/src/mol-model/volume/volume.ts b/src/mol-model/volume/volume.ts
index f61fed8e84ee6c8feacd8fdc86c5b38b5b9c3678..69afd4a966816b2fda54c68c82832eca8fd01981 100644
--- a/src/mol-model/volume/volume.ts
+++ b/src/mol-model/volume/volume.ts
@@ -4,32 +4,105 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { VolumeData, VolumeIsoValue } from './data';
+import { Grid } from './grid';
 import { OrderedSet } from '../../mol-data/int';
 import { Sphere3D } from '../../mol-math/geometry';
 import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
 import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
 import { CubeFormat } from '../../mol-model-formats/volume/cube';
+import { equalEps } from '../../mol-math/linear-algebra/3d/common';
+import { ModelFormat } from '../../mol-model-formats/format';
+import { CustomProperties } from '../custom-property';
+
+export interface Volume {
+    readonly label?: string
+    readonly grid: Grid
+    readonly sourceData: ModelFormat
+
+    // TODO use...
+    customProperties: CustomProperties
+
+    /**
+     * Not to be accessed directly, each custom property descriptor
+     * defines property accessors that use this field to store the data.
+     */
+    _propertyData: { [name: string]: any }
+
+    // TODO add as customProperty?
+    readonly colorVolume?: Volume
+}
 
 export namespace Volume {
     export type CellIndex = { readonly '@type': 'cell-index' } & number
 
-    export function isOrbitals(volume: VolumeData) {
+    export type IsoValue = IsoValue.Absolute | IsoValue.Relative
+
+    export namespace IsoValue {
+        export type Relative = Readonly<{ kind: 'relative', relativeValue: number }>
+        export type Absolute = Readonly<{ kind: 'absolute', absoluteValue: number }>
+
+        export function areSame(a: IsoValue, b: IsoValue, stats: Grid['stats']) {
+            return equalEps(toAbsolute(a, stats).absoluteValue, toAbsolute(b, stats).absoluteValue, stats.sigma / 100);
+        }
+
+        export function absolute(value: number): Absolute { return { kind: 'absolute', absoluteValue: value }; }
+        export function relative(value: number): Relative { return { kind: 'relative', relativeValue: value }; }
+
+        export function calcAbsolute(stats: Grid['stats'], relativeValue: number): number {
+            return relativeValue * stats.sigma + stats.mean;
+        }
+
+        export function calcRelative(stats: Grid['stats'], absoluteValue: number): number {
+            return stats.sigma === 0 ? 0 : ((absoluteValue - stats.mean) / stats.sigma);
+        }
+
+        export function toAbsolute(value: IsoValue, stats: Grid['stats']): Absolute {
+            return value.kind === 'absolute' ? value : { kind: 'absolute', absoluteValue: IsoValue.calcAbsolute(stats, value.relativeValue) };
+        }
+
+        export function toRelative(value: IsoValue, stats: Grid['stats']): Relative {
+            return value.kind === 'relative' ? value : { kind: 'relative', relativeValue: IsoValue.calcRelative(stats, value.absoluteValue) };
+        }
+
+        export function toString(value: IsoValue) {
+            return value.kind === 'relative'
+                ? `${value.relativeValue.toFixed(2)} σ`
+                : `${value.absoluteValue.toPrecision(4)}`;
+        }
+    }
+
+    export const One: Volume = {
+        label: '',
+        grid: Grid.One,
+        sourceData: { kind: '', name: '', data: {} },
+        customProperties: new CustomProperties(),
+        _propertyData: Object.create(null),
+    };
+
+    export function areEquivalent(volA: Volume, volB: Volume) {
+        return Grid.areEquivalent(volA.grid, volB.grid);
+    }
+
+    export function isEmpty(vol: Volume) {
+        return Grid.isEmpty(vol.grid);
+    }
+
+    export function isOrbitals(volume: Volume) {
         if (!CubeFormat.is(volume.sourceData)) return false;
         return volume.sourceData.data.header.orbitals;
     }
 
-    export interface Loci { readonly kind: 'volume-loci', readonly volume: VolumeData }
-    export function Loci(volume: VolumeData): Loci { return { kind: 'volume-loci', volume }; }
+    export interface Loci { readonly kind: 'volume-loci', readonly volume: Volume }
+    export function Loci(volume: Volume): Loci { return { kind: 'volume-loci', volume }; }
     export function isLoci(x: any): x is Loci { return !!x && x.kind === 'volume-loci'; }
     export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume; }
-    export function isLociEmpty(loci: Loci) { return loci.volume.data.data.length === 0; }
+    export function isLociEmpty(loci: Loci) { return Grid.isEmpty(loci.volume.grid); }
 
-    export function getBoundingSphere(volume: VolumeData, boundingSphere?: Sphere3D) {
+    export function getBoundingSphere(volume: Volume, boundingSphere?: Sphere3D) {
         if (!boundingSphere) boundingSphere = Sphere3D();
 
-        const transform = VolumeData.getGridToCartesianTransform(volume);
-        const [x, y, z] = volume.data.space.dimensions;
+        const transform = Grid.getGridToCartesianTransform(volume.grid);
+        const [x, y, z] = volume.grid.cells.space.dimensions;
 
         const cpA = Vec3.create(0, 0, 0); Vec3.transformMat4(cpA, cpA, transform);
         const cpB = Vec3.create(x, y, z); Vec3.transformMat4(cpB, cpB, transform);
@@ -52,31 +125,31 @@ export namespace Volume {
     }
 
     export namespace Isosurface {
-        export interface Loci { readonly kind: 'isosurface-loci', readonly volume: VolumeData, readonly isoValue: VolumeIsoValue }
-        export function Loci(volume: VolumeData, isoValue: VolumeIsoValue): Loci { return { kind: 'isosurface-loci', volume, isoValue }; }
+        export interface Loci { readonly kind: 'isosurface-loci', readonly volume: Volume, readonly isoValue: Volume.IsoValue }
+        export function Loci(volume: Volume, isoValue: Volume.IsoValue): Loci { return { kind: 'isosurface-loci', volume, isoValue }; }
         export function isLoci(x: any): x is Loci { return !!x && x.kind === 'isosurface-loci'; }
-        export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && VolumeIsoValue.areSame(a.isoValue, b.isoValue, a.volume.dataStats); }
-        export function isLociEmpty(loci: Loci) { return loci.volume.data.data.length === 0; }
+        export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && Volume.IsoValue.areSame(a.isoValue, b.isoValue, a.volume.grid.stats); }
+        export function isLociEmpty(loci: Loci) { return loci.volume.grid.cells.data.length === 0; }
 
-        export function getBoundingSphere(volume: VolumeData, isoValue: VolumeIsoValue, boundingSphere?: Sphere3D) {
+        export function getBoundingSphere(volume: Volume, isoValue: Volume.IsoValue, boundingSphere?: Sphere3D) {
             // TODO get bounding sphere for subgrid with values >= isoValue
             return Volume.getBoundingSphere(volume, boundingSphere);
         }
     }
 
     export namespace Cell {
-        export interface Loci { readonly kind: 'cell-loci', readonly volume: VolumeData, readonly indices: OrderedSet<CellIndex> }
-        export function Loci(volume: VolumeData, indices: OrderedSet<CellIndex>): Loci { return { kind: 'cell-loci', volume, indices }; }
+        export interface Loci { readonly kind: 'cell-loci', readonly volume: Volume, readonly indices: OrderedSet<CellIndex> }
+        export function Loci(volume: Volume, indices: OrderedSet<CellIndex>): Loci { return { kind: 'cell-loci', volume, indices }; }
         export function isLoci(x: any): x is Loci { return !!x && x.kind === 'cell-loci'; }
         export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && OrderedSet.areEqual(a.indices, b.indices); }
         export function isLociEmpty(loci: Loci) { return OrderedSet.size(loci.indices) === 0; }
 
         const boundaryHelper = new BoundaryHelper('98');
         const tmpBoundaryPos = Vec3();
-        export function getBoundingSphere(volume: VolumeData, indices: OrderedSet<CellIndex>, boundingSphere?: Sphere3D) {
+        export function getBoundingSphere(volume: Volume, indices: OrderedSet<CellIndex>, boundingSphere?: Sphere3D) {
             boundaryHelper.reset();
-            const transform = VolumeData.getGridToCartesianTransform(volume);
-            const { getCoords } = volume.data.space;
+            const transform = Grid.getGridToCartesianTransform(volume.grid);
+            const { getCoords } = volume.grid.cells.space;
 
             for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
                 const o = OrderedSet.getAt(indices, i);
diff --git a/src/mol-plugin-state/formats/volume.ts b/src/mol-plugin-state/formats/volume.ts
index b39e288e1e3ab02b87f22b5c307b160c8f3fb55a..853c32bd70a1359eaf85e0687b9564301a44c410 100644
--- a/src/mol-plugin-state/formats/volume.ts
+++ b/src/mol-plugin-state/formats/volume.ts
@@ -12,10 +12,9 @@ import { StateObjectSelector } from '../../mol-state';
 import { PluginStateObject } from '../objects';
 import { VolumeRepresentation3DHelpers } from '../transforms/representation';
 import { ColorNames } from '../../mol-util/color/names';
-import { VolumeIsoValue } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { createVolumeRepresentationParams } from '../helpers/volume-representation-params';
 import { objectForEach } from '../../mol-util/object';
-import { Volume } from '../../mol-model/volume/volume';
 
 const Category = 'Volume';
 
@@ -110,13 +109,13 @@ export const CubeProvider = DataFormatProvider({
         if (volumeData && Volume.isOrbitals(volumeData)) {
             const volumePos = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
                 type: 'isosurface',
-                typeParams: { isoValue: VolumeIsoValue.relative(1), alpha: 0.4 },
+                typeParams: { isoValue: Volume.IsoValue.relative(1), alpha: 0.4 },
                 color: 'uniform',
                 colorParams: { value: ColorNames.blue }
             }));
             const volumeNeg = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
                 type: 'isosurface',
-                typeParams: { isoValue: VolumeIsoValue.relative(-1), alpha: 0.4 },
+                typeParams: { isoValue: Volume.IsoValue.relative(-1), alpha: 0.4 },
                 color: 'uniform',
                 colorParams: { value: ColorNames.red }
             }));
@@ -124,7 +123,7 @@ export const CubeProvider = DataFormatProvider({
         } else {
             const volume = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
                 type: 'isosurface',
-                typeParams: { isoValue: VolumeIsoValue.relative(2), alpha: 0.4 },
+                typeParams: { isoValue: Volume.IsoValue.relative(2), alpha: 0.4 },
                 color: 'uniform',
                 colorParams: { value: ColorNames.grey }
             }));
@@ -176,13 +175,13 @@ export const DscifProvider = DataFormatProvider({
         if (volumes.length > 0) {
             visuals[0] = tree
                 .to(volumes[0])
-                .apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: VolumeIsoValue.relative(1.5), alpha: 1 }, 'uniform', { value: ColorNames.teal }))
+                .apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: Volume.IsoValue.relative(1.5), alpha: 1 }, 'uniform', { value: ColorNames.teal }))
                 .selector;
         }
 
         if (volumes.length > 1) {
-            const posParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: VolumeIsoValue.relative(3), alpha: 0.3 }, 'uniform', { value: ColorNames.green });
-            const negParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: VolumeIsoValue.relative(-3), alpha: 0.3 }, 'uniform', { value: ColorNames.red });
+            const posParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: Volume.IsoValue.relative(3), alpha: 0.3 }, 'uniform', { value: ColorNames.green });
+            const negParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: Volume.IsoValue.relative(-3), alpha: 0.3 }, 'uniform', { value: ColorNames.red });
             visuals[visuals.length] = tree.to(volumes[1]).apply(StateTransforms.Representation.VolumeRepresentation3D, posParams).selector;
             visuals[visuals.length] = tree.to(volumes[1]).apply(StateTransforms.Representation.VolumeRepresentation3D, negParams).selector;
         }
diff --git a/src/mol-plugin-state/helpers/volume-representation-params.ts b/src/mol-plugin-state/helpers/volume-representation-params.ts
index 3dae058f303eb03f87c8241432b44833533d3e83..55d9ab3bd2ffe0f71b03a101e8809825337c8f1b 100644
--- a/src/mol-plugin-state/helpers/volume-representation-params.ts
+++ b/src/mol-plugin-state/helpers/volume-representation-params.ts
@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { VolumeData } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { PluginContext } from '../../mol-plugin/context';
 import { RepresentationProvider } from '../../mol-repr/representation';
 import { VolumeRepresentationRegistry } from '../../mol-repr/volume/registry';
@@ -30,7 +30,7 @@ export interface VolumeRepresentationBuiltInProps<
 }
 
 export interface VolumeRepresentationProps<
-    R extends RepresentationProvider<VolumeData> = RepresentationProvider<VolumeData>,
+    R extends RepresentationProvider<Volume> = RepresentationProvider<Volume>,
     C extends ColorTheme.Provider = ColorTheme.Provider,
     S extends SizeTheme.Provider = SizeTheme.Provider> {
     type?: R,
@@ -41,43 +41,43 @@ export interface VolumeRepresentationProps<
     sizeParams?: Partial<SizeTheme.ParamValues<S>>
 }
 
-export function createVolumeRepresentationParams<R extends VolumeRepresentationRegistry.BuiltIn, C extends ColorTheme.BuiltIn, S extends SizeTheme.BuiltIn>(ctx: PluginContext, volume?: VolumeData, props?: VolumeRepresentationBuiltInProps<R, C, S>): StateTransformer.Params<VolumeRepresentation3D>
-export function createVolumeRepresentationParams<R extends RepresentationProvider<VolumeData>, C extends ColorTheme.Provider, S extends SizeTheme.Provider>(ctx: PluginContext, volume?: VolumeData, props?: VolumeRepresentationProps<R, C, S>): StateTransformer.Params<VolumeRepresentation3D>
-export function createVolumeRepresentationParams(ctx: PluginContext, volume?: VolumeData, props: any = {}): StateTransformer.Params<VolumeRepresentation3D>  {
+export function createVolumeRepresentationParams<R extends VolumeRepresentationRegistry.BuiltIn, C extends ColorTheme.BuiltIn, S extends SizeTheme.BuiltIn>(ctx: PluginContext, volume?: Volume, props?: VolumeRepresentationBuiltInProps<R, C, S>): StateTransformer.Params<VolumeRepresentation3D>
+export function createVolumeRepresentationParams<R extends RepresentationProvider<Volume>, C extends ColorTheme.Provider, S extends SizeTheme.Provider>(ctx: PluginContext, volume?: Volume, props?: VolumeRepresentationProps<R, C, S>): StateTransformer.Params<VolumeRepresentation3D>
+export function createVolumeRepresentationParams(ctx: PluginContext, volume?: Volume, props: any = {}): StateTransformer.Params<VolumeRepresentation3D>  {
     const p = props as VolumeRepresentationBuiltInProps;
-    if (typeof p.type === 'string' || typeof p.color === 'string' || typeof p.size === 'string') return createParamsByName(ctx, volume || VolumeData.One, props);
-    return createParamsProvider(ctx, volume || VolumeData.One, props);
+    if (typeof p.type === 'string' || typeof p.color === 'string' || typeof p.size === 'string') return createParamsByName(ctx, volume || Volume.One, props);
+    return createParamsProvider(ctx, volume || Volume.One, props);
 }
 
-export function getVolumeThemeTypes(ctx: PluginContext, volume?: VolumeData) {
+export function getVolumeThemeTypes(ctx: PluginContext, volume?: Volume) {
     const { themes: themeCtx } = ctx.representation.volume;
     if (!volume) return themeCtx.colorThemeRegistry.types;
     return themeCtx.colorThemeRegistry.getApplicableTypes({ volume });
 }
 
-export function createVolumeColorThemeParams<T extends ColorTheme.BuiltIn>(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName: T, params?: ColorTheme.BuiltInParams<T>): StateTransformer.Params<VolumeRepresentation3D>['colorTheme']
-export function createVolumeColorThemeParams(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['colorTheme']
-export function createVolumeColorThemeParams(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['colorTheme'] {
+export function createVolumeColorThemeParams<T extends ColorTheme.BuiltIn>(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName: T, params?: ColorTheme.BuiltInParams<T>): StateTransformer.Params<VolumeRepresentation3D>['colorTheme']
+export function createVolumeColorThemeParams(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['colorTheme']
+export function createVolumeColorThemeParams(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['colorTheme'] {
     const { registry, themes } = ctx.representation.volume;
     const repr = registry.get(typeName || registry.default.name);
     const color = themes.colorThemeRegistry.get(themeName || repr.defaultColorTheme.name);
-    const colorDefaultParams = PD.getDefaultValues(color.getParams({ volume: volume || VolumeData.One }));
+    const colorDefaultParams = PD.getDefaultValues(color.getParams({ volume: volume || Volume.One }));
     if (color.name === repr.defaultColorTheme.name) Object.assign(colorDefaultParams, repr.defaultColorTheme.props);
     return { name: color.name, params: Object.assign(colorDefaultParams, params) };
 }
 
-export function createVolumeSizeThemeParams<T extends SizeTheme.BuiltIn>(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName: T, params?: SizeTheme.BuiltInParams<T>): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme']
-export function createVolumeSizeThemeParams(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme']
-export function createVolumeSizeThemeParams(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme'] {
+export function createVolumeSizeThemeParams<T extends SizeTheme.BuiltIn>(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName: T, params?: SizeTheme.BuiltInParams<T>): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme']
+export function createVolumeSizeThemeParams(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme']
+export function createVolumeSizeThemeParams(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme'] {
     const { registry, themes } = ctx.representation.volume;
     const repr = registry.get(typeName || registry.default.name);
     const size = themes.sizeThemeRegistry.get(themeName || repr.defaultSizeTheme.name);
-    const sizeDefaultParams = PD.getDefaultValues(size.getParams({ volume: volume || VolumeData.One }));
+    const sizeDefaultParams = PD.getDefaultValues(size.getParams({ volume: volume || Volume.One }));
     if (size.name === repr.defaultSizeTheme.name) Object.assign(sizeDefaultParams, repr.defaultSizeTheme.props);
     return { name: size.name, params: Object.assign(sizeDefaultParams, params) };
 }
 
-function createParamsByName(ctx: PluginContext, volume: VolumeData, props: VolumeRepresentationBuiltInProps): StateTransformer.Params<VolumeRepresentation3D> {
+function createParamsByName(ctx: PluginContext, volume: Volume, props: VolumeRepresentationBuiltInProps): StateTransformer.Params<VolumeRepresentation3D> {
     const typeProvider = (props.type && ctx.representation.volume.registry.get(props.type))
         || ctx.representation.volume.registry.default.provider;
     const colorProvider = (props.color && ctx.representation.volume.themes.colorThemeRegistry.get(props.color))
@@ -95,7 +95,7 @@ function createParamsByName(ctx: PluginContext, volume: VolumeData, props: Volum
     });
 }
 
-function createParamsProvider(ctx: PluginContext, volume: VolumeData, props: VolumeRepresentationProps = {}): StateTransformer.Params<VolumeRepresentation3D> {
+function createParamsProvider(ctx: PluginContext, volume: Volume, props: VolumeRepresentationProps = {}): StateTransformer.Params<VolumeRepresentation3D> {
     const { themes: themeCtx } = ctx.representation.volume;
     const themeDataCtx = { volume };
 
diff --git a/src/mol-plugin-state/objects.ts b/src/mol-plugin-state/objects.ts
index 96d3488f4cf6034bfc6399e8f2533babc2bc680a..a9b18d77da426094dc91deb5d34415de6973afe6 100644
--- a/src/mol-plugin-state/objects.ts
+++ b/src/mol-plugin-state/objects.ts
@@ -14,7 +14,7 @@ import { PlyFile } from '../mol-io/reader/ply/schema';
 import { PsfFile } from '../mol-io/reader/psf/parser';
 import { ShapeProvider } from '../mol-model/shape/provider';
 import { Coordinates as _Coordinates, Model as _Model, Structure as _Structure, StructureElement, Topology as _Topology } from '../mol-model/structure';
-import { VolumeData } from '../mol-model/volume';
+import { Volume as _Volume } from '../mol-model/volume';
 import { PluginBehavior } from '../mol-plugin/behavior/behavior';
 import { Representation } from '../mol-repr/representation';
 import { ShapeRepresentation } from '../mol-repr/shape/representation';
@@ -121,7 +121,7 @@ export namespace PluginStateObject {
     }
 
     export namespace Volume {
-        export class Data extends Create<VolumeData>({ name: 'Volume Data', typeClass: 'Object' }) { }
+        export class Data extends Create<_Volume>({ name: 'Volume', typeClass: 'Object' }) { }
         export class Representation3D extends CreateRepresentation3D<VolumeRepresentation<any>>({ name: 'Volume 3D' }) { }
     }
 
diff --git a/src/mol-plugin-state/transforms/representation.ts b/src/mol-plugin-state/transforms/representation.ts
index 0aee3349c202b7517cfec32d605203178b652b44..bcb1f8560550c73feba8220fa5b5cd7ac932c95b 100644
--- a/src/mol-plugin-state/transforms/representation.ts
+++ b/src/mol-plugin-state/transforms/representation.ts
@@ -6,7 +6,7 @@
  */
 
 import { Structure, StructureElement } from '../../mol-model/structure';
-import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { PluginContext } from '../../mol-plugin/context';
 import { VolumeRepresentationRegistry } from '../../mol-repr/volume/registry';
 import { VolumeParams } from '../../mol-repr/volume/representation';
@@ -441,7 +441,7 @@ const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.Bui
 //
 
 export namespace VolumeRepresentation3DHelpers {
-    export function getDefaultParams(ctx: PluginContext, name: VolumeRepresentationRegistry.BuiltIn, volume: VolumeData, volumeParams?: Partial<PD.Values<VolumeParams>>): StateTransformer.Params<VolumeRepresentation3D> {
+    export function getDefaultParams(ctx: PluginContext, name: VolumeRepresentationRegistry.BuiltIn, volume: Volume, volumeParams?: Partial<PD.Values<VolumeParams>>): StateTransformer.Params<VolumeRepresentation3D> {
         const type = ctx.representation.volume.registry.get(name);
 
         const themeDataCtx = { volume };
@@ -467,7 +467,7 @@ export namespace VolumeRepresentation3DHelpers {
     }
 
     export function getDescription(props: any) {
-        return props.isoValue && VolumeIsoValue.toString(props.isoValue);
+        return props.isoValue && Volume.IsoValue.toString(props.isoValue);
     }
 }
 type VolumeRepresentation3D = typeof VolumeRepresentation3D
@@ -485,16 +485,16 @@ const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
                 type: PD.Mapped<any>(
                     registry.default.name,
                     registry.types,
-                    name => PD.Group<any>(registry.get(name).getParams(themeCtx, VolumeData.One))),
+                    name => PD.Group<any>(registry.get(name).getParams(themeCtx, Volume.One))),
                 colorTheme: PD.Mapped<any>(
                     type.defaultColorTheme.name,
                     themeCtx.colorThemeRegistry.types,
-                    name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ volume: VolumeData.One }))
+                    name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ volume: Volume.One }))
                 ),
                 sizeTheme: PD.Mapped<any>(
                     type.defaultSizeTheme.name,
                     themeCtx.sizeThemeRegistry.types,
-                    name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ volume: VolumeData.One }))
+                    name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ volume: Volume.One }))
                 )
             };
         }
diff --git a/src/mol-plugin-state/transforms/volume.ts b/src/mol-plugin-state/transforms/volume.ts
index 1e85fb0bdcf91d5ddfcab18b9ba9340e8fb2df47..e9cee9706606348e5ba87c144b9af2485ae478c4 100644
--- a/src/mol-plugin-state/transforms/volume.ts
+++ b/src/mol-plugin-state/transforms/volume.ts
@@ -15,7 +15,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { PluginStateObject as SO, PluginStateTransform } from '../objects';
 import { volumeFromCube } from '../../mol-model-formats/volume/cube';
 import { volumeFromDx } from '../../mol-model-formats/volume/dx';
-import { VolumeData } from '../../mol-model/volume';
+import {  Volume } from '../../mol-model/volume';
 import { PluginContext } from '../../mol-plugin/context';
 import { StateSelection } from '../../mol-state';
 
@@ -160,8 +160,8 @@ const AssignColorVolume = PluginStateTransform.BuiltIn({
             if (!dependencies || !dependencies[params.ref]) {
                 throw new Error('Dependency not available.');
             }
-            const colorVolume = dependencies[params.ref].data as VolumeData;
-            const volume: VolumeData = {
+            const colorVolume = dependencies[params.ref].data as Volume;
+            const volume: Volume = {
                 ...a.data,
                 colorVolume
             };
diff --git a/src/mol-plugin-ui/custom/volume.tsx b/src/mol-plugin-ui/custom/volume.tsx
index 261ede7e7b7709ebb99daf70db2b906da2dfba0e..b0beddb56e50e12f5523f5cab83d913d595b5622 100644
--- a/src/mol-plugin-ui/custom/volume.tsx
+++ b/src/mol-plugin-ui/custom/volume.tsx
@@ -12,7 +12,7 @@ import { ExpandableControlRow, IconButton } from '../controls/common';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ParameterControls, ParamOnChange } from '../controls/parameters';
 import { Slider } from '../controls/slider';
-import { VolumeIsoValue, VolumeData } from '../../mol-model/volume';
+import { Volume, Grid } from '../../mol-model/volume';
 import { Vec3 } from '../../mol-math/linear-algebra';
 import { ColorNames } from '../../mol-util/color/names';
 import { toPrecision } from '../../mol-util/number';
@@ -34,7 +34,7 @@ class Channel extends PluginUIComponent<{
     channels: { [k: string]: VolumeStreaming.ChannelParams },
     isRelative: boolean,
     params: StateTransformParameters.Props,
-    stats: VolumeData['dataStats'],
+    stats: Grid['stats'],
     changeIso: (name: string, value: number, isRelative: boolean) => void,
     changeParams: (name: string, param: string, value: any) => void,
     bCell: StateObjectCell,
@@ -111,7 +111,7 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
                         ...old.entry.params.channels,
                         [name]: {
                             ...(old.entry.params.channels as any)[name],
-                            isoValue: isRelative ? VolumeIsoValue.relative(value) : VolumeIsoValue.absolute(value)
+                            isoValue: isRelative ? Volume.IsoValue.relative(value) : Volume.IsoValue.absolute(value)
                         }
                     }
                 }
@@ -139,11 +139,11 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
         });
     };
 
-    convert(channel: any, stats: VolumeData['dataStats'], isRelative: boolean) {
+    convert(channel: any, stats: Grid['stats'], isRelative: boolean) {
         return {
             ...channel, isoValue: isRelative
-                ? VolumeIsoValue.toRelative(channel.isoValue, stats)
-                : VolumeIsoValue.toAbsolute(channel.isoValue, stats)
+                ? Volume.IsoValue.toRelative(channel.isoValue, stats)
+                : Volume.IsoValue.toAbsolute(channel.isoValue, stats)
         };
     }
 
@@ -215,7 +215,7 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
         const params = this.props.params as VolumeStreaming.Params;
         const detailLevel = ((this.props.info.params as VolumeStreaming.ParamDefinition)
             .entry.map(params.entry.name) as PD.Group<VolumeStreaming.EntryParamDefinition>).params.detailLevel;
-        const isRelative = ((params.entry.params.channels as any)[pivot].isoValue as VolumeIsoValue).kind === 'relative';
+        const isRelative = ((params.entry.params.channels as any)[pivot].isoValue as Volume.IsoValue).kind === 'relative';
 
         const sampling = b.info.header.sampling[0];
 
diff --git a/src/mol-plugin-ui/structure/volume.tsx b/src/mol-plugin-ui/structure/volume.tsx
index 4c1eb90d848eae0574e854656fae41f7d9832924..3291975cbdb9101f1c0c008dd73fab3725b62d32 100644
--- a/src/mol-plugin-ui/structure/volume.tsx
+++ b/src/mol-plugin-ui/structure/volume.tsx
@@ -137,7 +137,7 @@ export class VolumeSourceControls extends CollapsableControls<{}, VolumeSourceCo
     private item = (ref: VolumeRef) => {
         const selected = this.plugin.managers.volume.hierarchy.selection;
 
-        const label = ref.cell.obj?.data.label || 'Volume';
+        const label = ref.cell.obj?.label || 'Volume';
         const item: ActionMenu.Item = { kind: 'item', label: label || ref.kind, selected: selected === ref, value: ref };
         return item;
     }
@@ -172,7 +172,7 @@ export class VolumeSourceControls extends CollapsableControls<{}, VolumeSourceCo
     get label() {
         const selected = this.plugin.managers.volume.hierarchy.selection;
         if (!selected) return 'Nothing Selected';
-        return selected?.cell.obj?.data.label || 'Volume';
+        return selected?.cell.obj?.label || 'Volume';
     }
 
     selectCurrent: ActionMenu.OnSelect = (item) => {
diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts
index 20648b515f4e77e492d700226377a722e288a3ba..b0d271f709c1fed8e6e610d01ee2842a53f71b6c 100644
--- a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts
+++ b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts
@@ -7,7 +7,7 @@
 
 import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
 import { PluginStateObject } from '../../../../mol-plugin-state/objects';
-import { VolumeIsoValue, VolumeData } from '../../../../mol-model/volume';
+import { Volume, Grid } from '../../../../mol-model/volume';
 import { createIsoValueParam } from '../../../../mol-repr/volume/isosurface';
 import { VolumeServerHeader, VolumeServerInfo } from './model';
 import { Box3D } from '../../../../mol-math/geometry';
@@ -31,13 +31,13 @@ export namespace VolumeStreaming {
     export const RootTag = 'volume-streaming-info';
 
     export interface ChannelParams {
-        isoValue: VolumeIsoValue,
+        isoValue: Volume.IsoValue,
         color: Color,
         wireframe: boolean,
         opacity: number
     }
 
-    function channelParam(label: string, color: Color, defaultValue: VolumeIsoValue, stats: VolumeData['dataStats'], defaults: Partial<ChannelParams> = {}) {
+    function channelParam(label: string, color: Color, defaultValue: Volume.IsoValue, stats: Grid['stats'], defaults: Partial<ChannelParams> = {}) {
         return PD.Group<ChannelParams>({
             isoValue: createIsoValueParam(typeof defaults.isoValue !== 'undefined' ? defaults.isoValue : defaultValue, stats),
             color: PD.Color(typeof defaults.color !== 'undefined' ? defaults.color : color),
@@ -71,7 +71,7 @@ export namespace VolumeStreaming {
         const { entryData, defaultView, structure, channelParams = { } } = options;
 
         // fake the info
-        const info = entryData || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: VolumeIsoValue.relative(0) };
+        const info = entryData || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: Volume.IsoValue.relative(0) };
         const box = (structure && structure.boundary.box) || Box3D.empty();
 
         return {
@@ -93,12 +93,12 @@ export namespace VolumeStreaming {
                 info.header.availablePrecisions.map((p, i) => [i, `${i + 1} [ ${Math.pow(p.maxVoxels, 1 / 3) | 0}^3 cells ]`] as [number, string]), { description: 'Determines the maximum number of voxels. Depending on the size of the volume options are in the range from 0 (0.52M voxels) to 6 (25.17M voxels).' }),
             channels: info.kind === 'em'
                 ? PD.Group({
-                    'em': channelParam('EM', Color(0x638F8F), info.emDefaultContourLevel || VolumeIsoValue.relative(1), info.header.sampling[0].valuesInfo[0], channelParams['em'])
+                    'em': channelParam('EM', Color(0x638F8F), info.emDefaultContourLevel || Volume.IsoValue.relative(1), info.header.sampling[0].valuesInfo[0], channelParams['em'])
                 }, { isFlat: true })
                 : PD.Group({
-                    '2fo-fc': channelParam('2Fo-Fc', Color(0x3362B2), VolumeIsoValue.relative(1.5), info.header.sampling[0].valuesInfo[0], channelParams['2fo-fc']),
-                    'fo-fc(+ve)': channelParam('Fo-Fc(+ve)', Color(0x33BB33), VolumeIsoValue.relative(3), info.header.sampling[0].valuesInfo[1], channelParams['fo-fc(+ve)']),
-                    'fo-fc(-ve)': channelParam('Fo-Fc(-ve)', Color(0xBB3333), VolumeIsoValue.relative(-3), info.header.sampling[0].valuesInfo[1], channelParams['fo-fc(-ve)']),
+                    '2fo-fc': channelParam('2Fo-Fc', Color(0x3362B2), Volume.IsoValue.relative(1.5), info.header.sampling[0].valuesInfo[0], channelParams['2fo-fc']),
+                    'fo-fc(+ve)': channelParam('Fo-Fc(+ve)', Color(0x33BB33), Volume.IsoValue.relative(3), info.header.sampling[0].valuesInfo[1], channelParams['fo-fc(+ve)']),
+                    'fo-fc(-ve)': channelParam('Fo-Fc(-ve)', Color(0xBB3333), Volume.IsoValue.relative(-3), info.header.sampling[0].valuesInfo[1], channelParams['fo-fc(-ve)']),
                 }, { isFlat: true }),
         };
     }
@@ -110,16 +110,16 @@ export namespace VolumeStreaming {
     export type ParamDefinition = typeof createParams extends (...args: any[]) => (infer T) ? T : never
     export type Params = ParamDefinition extends PD.Params ? PD.Values<ParamDefinition> : {}
 
-    type ChannelsInfo = { [name in ChannelType]?: { isoValue: VolumeIsoValue, color: Color, wireframe: boolean, opacity: number } }
-    type ChannelsData = { [name in 'EM' | '2FO-FC' | 'FO-FC']?: VolumeData }
+    type ChannelsInfo = { [name in ChannelType]?: { isoValue: Volume.IsoValue, color: Color, wireframe: boolean, opacity: number } }
+    type ChannelsData = { [name in 'EM' | '2FO-FC' | 'FO-FC']?: Volume }
 
     export type ChannelType = 'em' | '2fo-fc' | 'fo-fc(+ve)' | 'fo-fc(-ve)'
     export const ChannelTypeOptions: [ChannelType, string][] = [['em', 'em'], ['2fo-fc', '2fo-fc'], ['fo-fc(+ve)', 'fo-fc(+ve)'], ['fo-fc(-ve)', 'fo-fc(-ve)']];
     export interface ChannelInfo {
-        data: VolumeData,
+        data: Volume,
         color: Color,
         wireframe: boolean,
-        isoValue: VolumeIsoValue.Relative,
+        isoValue: Volume.IsoValue.Relative,
         opacity: number
     }
     export type Channels = { [name in ChannelType]?: ChannelInfo }
@@ -329,24 +329,24 @@ export namespace VolumeStreaming {
             const info = params.entry.params.channels as ChannelsInfo;
 
             if (this.info.kind === 'x-ray') {
-                this.channels['2fo-fc'] = this.createChannel(data['2FO-FC'] || VolumeData.One, info['2fo-fc'], this.info.header.sampling[0].valuesInfo[0]);
-                this.channels['fo-fc(+ve)'] = this.createChannel(data['FO-FC'] || VolumeData.One, info['fo-fc(+ve)'], this.info.header.sampling[0].valuesInfo[1]);
-                this.channels['fo-fc(-ve)'] = this.createChannel(data['FO-FC'] || VolumeData.One, info['fo-fc(-ve)'], this.info.header.sampling[0].valuesInfo[1]);
+                this.channels['2fo-fc'] = this.createChannel(data['2FO-FC'] || Volume.One, info['2fo-fc'], this.info.header.sampling[0].valuesInfo[0]);
+                this.channels['fo-fc(+ve)'] = this.createChannel(data['FO-FC'] || Volume.One, info['fo-fc(+ve)'], this.info.header.sampling[0].valuesInfo[1]);
+                this.channels['fo-fc(-ve)'] = this.createChannel(data['FO-FC'] || Volume.One, info['fo-fc(-ve)'], this.info.header.sampling[0].valuesInfo[1]);
             } else {
-                this.channels['em'] = this.createChannel(data['EM'] || VolumeData.One, info['em'], this.info.header.sampling[0].valuesInfo[0]);
+                this.channels['em'] = this.createChannel(data['EM'] || Volume.One, info['em'], this.info.header.sampling[0].valuesInfo[0]);
             }
 
             return true;
         }
 
-        private createChannel(data: VolumeData, info: ChannelsInfo['em'], stats: VolumeData['dataStats']): ChannelInfo {
+        private createChannel(data: Volume, info: ChannelsInfo['em'], stats: Grid['stats']): ChannelInfo {
             const i = info!;
             return {
                 data,
                 color: i.color,
                 wireframe: i.wireframe,
                 opacity: i.opacity,
-                isoValue: i.isoValue.kind === 'relative' ? i.isoValue : VolumeIsoValue.toRelative(i.isoValue, stats)
+                isoValue: i.isoValue.kind === 'relative' ? i.isoValue : Volume.IsoValue.toRelative(i.isoValue, stats)
             };
         }
 
diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/model.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/model.ts
index 59f47b1534cc99394ea252a5a9f3f629992cf13f..13138d154e3fbac6335004781e055d723260f434 100644
--- a/src/mol-plugin/behavior/dynamic/volume-streaming/model.ts
+++ b/src/mol-plugin/behavior/dynamic/volume-streaming/model.ts
@@ -1,12 +1,12 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { PluginStateObject } from '../../../../mol-plugin-state/objects';
-import { VolumeIsoValue } from '../../../../mol-model/volume';
+import { Volume } from '../../../../mol-model/volume';
 import { Structure } from '../../../../mol-model/structure';
 
 export class VolumeServerInfo extends PluginStateObject.Create<VolumeServerInfo.Data>({ name: 'Volume Streaming', typeClass: 'Object' }) { }
@@ -18,7 +18,7 @@ export namespace VolumeServerInfo {
         // for em, the EMDB access code, for x-ray, the PDB id
         dataId: string,
         header: VolumeServerHeader,
-        emDefaultContourLevel?: VolumeIsoValue,
+        emDefaultContourLevel?: Volume.IsoValue,
     }
     export interface Data {
         serverUrl: string,
diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
index d5d27521c32d13fdea57853ccc59b1e9efb30486..fdbf2f7ec8a2dd9de276586fe013268b80b007a5 100644
--- a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
+++ b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
@@ -12,7 +12,7 @@ import { Task } from '../../../../mol-task';
 import { PluginContext } from '../../../../mol-plugin/context';
 import { urlCombine } from '../../../../mol-util/url';
 import { createIsoValueParam } from '../../../../mol-repr/volume/isosurface';
-import { VolumeIsoValue } from '../../../../mol-model/volume';
+import { Volume } from '../../../../mol-model/volume';
 import { StateAction, StateObject, StateTransformer } from '../../../../mol-state';
 import { getStreamingMethod, getIds, getContourLevel, getEmdbIds } from './util';
 import { VolumeStreaming } from './behavior';
@@ -27,7 +27,7 @@ import { Model } from '../../../../mol-model/structure';
 function addEntry(entries: InfoEntryProps[], method: VolumeServerInfo.Kind, dataId: string, emDefaultContourLevel: number) {
     entries.push({
         source: method === 'em'
-            ? { name: 'em', params: { isoValue: VolumeIsoValue.absolute(emDefaultContourLevel || 0) } }
+            ? { name: 'em', params: { isoValue: Volume.IsoValue.absolute(emDefaultContourLevel || 0) } }
             : { name: 'x-ray', params: { } },
         dataId
     });
@@ -157,7 +157,7 @@ const InfoEntryParams = {
     dataId: PD.Text(''),
     source: PD.MappedStatic('x-ray', {
         'em': PD.Group({
-            isoValue: createIsoValueParam(VolumeIsoValue.relative(1))
+            isoValue: createIsoValueParam(Volume.IsoValue.relative(1))
         }),
         'x-ray': PD.Group({ })
     })
@@ -185,7 +185,7 @@ const CreateVolumeStreamingInfo = PluginStateTransform.BuiltIn({
         for (let i = 0, il = params.entries.length; i < il; ++i) {
             const e = params.entries[i];
             const dataId = e.dataId;
-            const emDefaultContourLevel = e.source.name === 'em' ? e.source.params.isoValue : VolumeIsoValue.relative(1);
+            const emDefaultContourLevel = e.source.name === 'em' ? e.source.params.isoValue : Volume.IsoValue.relative(1);
             await taskCtx.update('Getting server header...');
             const header = await plugin.fetch({ url: urlCombine(params.serverUrl, `${e.source.name}/${dataId.toLocaleLowerCase()}`), type: 'json' }).runInContext(taskCtx) as VolumeServerHeader;
             entries.push({
diff --git a/src/mol-repr/volume/direct-volume.ts b/src/mol-repr/volume/direct-volume.ts
index f896bdbc1bb1b95b3b464495a89fc28bf4cb1dd2..da3cc458a499e22939392ddf591e305bad038113 100644
--- a/src/mol-repr/volume/direct-volume.ts
+++ b/src/mol-repr/volume/direct-volume.ts
@@ -7,7 +7,7 @@
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
 import { Box3D } from '../../mol-math/geometry';
-import { VolumeData } from '../../mol-model/volume';
+import { Grid, Volume } from '../../mol-model/volume';
 import { RuntimeContext } from '../../mol-task';
 import { WebGLContext } from '../../mol-gl/webgl/context';
 import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
@@ -20,7 +20,6 @@ import { NullLocation } from '../../mol-model/location';
 import { EmptyLoci } from '../../mol-model/loci';
 import { VisualUpdateState } from '../util';
 import { RepresentationContext, RepresentationParamsGetter } from '../representation';
-import { Volume } from '../../mol-model/volume/volume';
 
 function getBoundingBox(gridDimension: Vec3, transform: Mat4) {
     const bbox = Box3D.empty();
@@ -49,9 +48,8 @@ function getVolumeTexture2dLayout(dim: Vec3, maxTextureSize: number) {
     return { width, height, columns, rows };
 }
 
-function createVolumeTexture2d(volume: VolumeData, maxTextureSize: number) {
-    const { data: tensor, dataStats: stats } = volume;
-    const { space, data } = tensor;
+function createVolumeTexture2d(volume: Volume, maxTextureSize: number) {
+    const { cells: { space, data }, stats } = volume.grid;
     const dim = space.dimensions as Vec3;
     const { get } = space;
     const { width, height, columns, rows } = getVolumeTexture2dLayout(dim, maxTextureSize);
@@ -85,11 +83,11 @@ function createVolumeTexture2d(volume: VolumeData, maxTextureSize: number) {
     return textureImage;
 }
 
-export function createDirectVolume2d(ctx: RuntimeContext, webgl: WebGLContext, volume: VolumeData, directVolume?: DirectVolume) {
-    const gridDimension = volume.data.space.dimensions as Vec3;
+export function createDirectVolume2d(ctx: RuntimeContext, webgl: WebGLContext, volume: Volume, directVolume?: DirectVolume) {
+    const gridDimension = volume.grid.cells.space.dimensions as Vec3;
     const textureImage = createVolumeTexture2d(volume, webgl.maxTextureSize);
     // debugTexture(createImageData(textureImage.array, textureImage.width, textureImage.height), 1/3)
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     const bbox = getBoundingBox(gridDimension, transform);
     const dim = Vec3.create(gridDimension[0], gridDimension[1], gridDimension[2]);
     dim[0] += 1; // horizontal padding
@@ -103,9 +101,8 @@ export function createDirectVolume2d(ctx: RuntimeContext, webgl: WebGLContext, v
 
 // 3d volume texture
 
-function createVolumeTexture3d(volume: VolumeData) {
-    const { data: tensor, dataStats: stats } = volume;
-    const { space, data } = tensor;
+function createVolumeTexture3d(volume: Volume) {
+    const { cells: { space, data }, stats } = volume.grid;
     const [ width, height, depth ] = space.dimensions as Vec3;
     const { get } = space;
 
@@ -128,10 +125,10 @@ function createVolumeTexture3d(volume: VolumeData) {
     return textureVolume;
 }
 
-export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, volume: VolumeData, directVolume?: DirectVolume) {
-    const gridDimension = volume.data.space.dimensions as Vec3;
+export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, volume: Volume, directVolume?: DirectVolume) {
+    const gridDimension = volume.grid.cells.space.dimensions as Vec3;
     const textureVolume = createVolumeTexture3d(volume);
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     // Mat4.invert(transform, transform)
     const bbox = getBoundingBox(gridDimension, transform);
 
@@ -143,7 +140,7 @@ export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, v
 
 //
 
-export async function createDirectVolume(ctx: VisualContext, volume: VolumeData, theme: Theme, props: PD.Values<DirectVolumeParams>, directVolume?: DirectVolume) {
+export async function createDirectVolume(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<DirectVolumeParams>, directVolume?: DirectVolume) {
     const { runtime, webgl } = ctx;
     if (webgl === undefined) throw new Error('DirectVolumeVisual requires `webgl` in props');
 
@@ -152,7 +149,7 @@ export async function createDirectVolume(ctx: VisualContext, volume: VolumeData,
         createDirectVolume2d(runtime, webgl, volume, directVolume);
 }
 
-function getLoci(volume: VolumeData, props: PD.Values<DirectVolumeParams>) {
+function getLoci(volume: Volume, props: PD.Values<DirectVolumeParams>) {
     return Volume.Loci(volume);
 }
 
@@ -163,7 +160,7 @@ export const DirectVolumeParams = {
     ...DirectVolume.Params
 };
 export type DirectVolumeParams = typeof DirectVolumeParams
-export function getDirectVolumeParams(ctx: ThemeRegistryContext, volume: VolumeData) {
+export function getDirectVolumeParams(ctx: ThemeRegistryContext, volume: Volume) {
     return PD.clone(DirectVolumeParams);
 }
 
@@ -171,16 +168,16 @@ export function DirectVolumeVisual(materialId: number): VolumeVisual<DirectVolum
     return VolumeVisual<DirectVolume, DirectVolumeParams>({
         defaultProps: PD.getDefaultValues(DirectVolumeParams),
         createGeometry: createDirectVolume,
-        createLocationIterator: (volume: VolumeData) => LocationIterator(1, 1, () => NullLocation),
+        createLocationIterator: (volume: Volume) => LocationIterator(1, 1, () => NullLocation),
         getLoci: () => EmptyLoci,
         eachLocation: () => false,
-        setUpdateState: (state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<DirectVolumeParams>, currentProps: PD.Values<DirectVolumeParams>) => {
+        setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<DirectVolumeParams>, currentProps: PD.Values<DirectVolumeParams>) => {
         },
         geometryUtils: DirectVolume.Utils
     }, materialId);
 }
 
-export function DirectVolumeRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, DirectVolumeParams>): VolumeRepresentation<DirectVolumeParams> {
+export function DirectVolumeRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, DirectVolumeParams>): VolumeRepresentation<DirectVolumeParams> {
     return VolumeRepresentation('Direct Volume', ctx, getParams, DirectVolumeVisual, getLoci);
 }
 
@@ -193,5 +190,5 @@ export const DirectVolumeRepresentationProvider = VolumeRepresentationProvider({
     defaultValues: PD.getDefaultValues(DirectVolumeParams),
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
-    isApplicable: (volume: VolumeData) => volume.data.data.length > 0
+    isApplicable: (volume: Volume) => !Volume.isEmpty(volume)
 });
\ No newline at end of file
diff --git a/src/mol-repr/volume/isosurface.ts b/src/mol-repr/volume/isosurface.ts
index 6e6ddb7f9245cddb287032b96d41026e6abdc6b6..65d5bac070e5846e30c6c331a3455d7e988b88e5 100644
--- a/src/mol-repr/volume/isosurface.ts
+++ b/src/mol-repr/volume/isosurface.ts
@@ -6,7 +6,7 @@
  */
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
+import { Grid, Volume } from '../../mol-model/volume';
 import { VisualContext } from '../visual';
 import { Theme, ThemeRegistryContext } from '../../mol-theme/theme';
 import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
@@ -18,15 +18,14 @@ import { VisualUpdateState } from '../util';
 import { Lines } from '../../mol-geo/geometry/lines/lines';
 import { RepresentationContext, RepresentationParamsGetter, Representation } from '../representation';
 import { toPrecision } from '../../mol-util/number';
-import { Volume } from '../../mol-model/volume/volume';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci } from '../../mol-model/loci';
 import { Interval, OrderedSet } from '../../mol-data/int';
 import { Tensor } from '../../mol-math/linear-algebra';
 import { fillSerial } from '../../mol-util/array';
 
-const defaultStats: VolumeData['dataStats'] = { min: -1, max: 1, mean: 0, sigma: 0.1  };
-export function createIsoValueParam(defaultValue: VolumeIsoValue, stats?: VolumeData['dataStats']) {
+const defaultStats: Grid['stats'] = { min: -1, max: 1, mean: 0, sigma: 0.1  };
+export function createIsoValueParam(defaultValue: Volume.IsoValue, stats?: Grid['stats']) {
     const sts = stats || defaultStats;
     const { min, max, mean, sigma } = sts;
 
@@ -36,34 +35,34 @@ export function createIsoValueParam(defaultValue: VolumeIsoValue, stats?: Volume
 
     let def = defaultValue;
     if (defaultValue.kind === 'absolute') {
-        if (defaultValue.absoluteValue < min) def = VolumeIsoValue.absolute(min);
-        else if (defaultValue.absoluteValue > max) def = VolumeIsoValue.absolute(max);
+        if (defaultValue.absoluteValue < min) def = Volume.IsoValue.absolute(min);
+        else if (defaultValue.absoluteValue > max) def = Volume.IsoValue.absolute(max);
     } else {
-        if (defaultValue.relativeValue < relMin) def = VolumeIsoValue.relative(relMin);
-        else if (defaultValue.relativeValue > relMax) def = VolumeIsoValue.relative(relMax);
+        if (defaultValue.relativeValue < relMin) def = Volume.IsoValue.relative(relMin);
+        else if (defaultValue.relativeValue > relMax) def = Volume.IsoValue.relative(relMax);
     }
 
     return PD.Conditioned(
         def,
         {
             'absolute': PD.Converted(
-                (v: VolumeIsoValue) => VolumeIsoValue.toAbsolute(v, VolumeData.One.dataStats).absoluteValue,
-                (v: number) => VolumeIsoValue.absolute(v),
+                (v: Volume.IsoValue) => Volume.IsoValue.toAbsolute(v, Grid.One.stats).absoluteValue,
+                (v: number) => Volume.IsoValue.absolute(v),
                 PD.Numeric(mean, { min, max, step: toPrecision(sigma / 100, 2) })
             ),
             'relative': PD.Converted(
-                (v: VolumeIsoValue) => VolumeIsoValue.toRelative(v, VolumeData.One.dataStats).relativeValue,
-                (v: number) => VolumeIsoValue.relative(v),
+                (v: Volume.IsoValue) => Volume.IsoValue.toRelative(v, Grid.One.stats).relativeValue,
+                (v: number) => Volume.IsoValue.relative(v),
                 PD.Numeric(Math.min(1, relMax), { min: relMin, max: relMax, step: toPrecision(Math.round(((max - min) / sigma)) / 100, 2) })
             )
         },
-        (v: VolumeIsoValue) => v.kind === 'absolute' ? 'absolute' : 'relative',
-        (v: VolumeIsoValue, c: 'absolute' | 'relative') => c === 'absolute' ? VolumeIsoValue.toAbsolute(v, sts) : VolumeIsoValue.toRelative(v, sts),
+        (v: Volume.IsoValue) => v.kind === 'absolute' ? 'absolute' : 'relative',
+        (v: Volume.IsoValue, c: 'absolute' | 'relative') => c === 'absolute' ? Volume.IsoValue.toAbsolute(v, sts) : Volume.IsoValue.toRelative(v, sts),
         { isEssential: true }
     );
 }
 
-export const IsoValueParam = createIsoValueParam(VolumeIsoValue.relative(2));
+export const IsoValueParam = createIsoValueParam(Volume.IsoValue.relative(2));
 type IsoValueParam = typeof IsoValueParam
 
 export const VolumeIsosurfaceParams = {
@@ -72,11 +71,11 @@ export const VolumeIsosurfaceParams = {
 export type VolumeIsosurfaceParams = typeof VolumeIsosurfaceParams
 export type VolumeIsosurfaceProps = PD.Values<VolumeIsosurfaceParams>
 
-function getLoci(volume: VolumeData, props: VolumeIsosurfaceProps) {
+function getLoci(volume: Volume, props: VolumeIsosurfaceProps) {
     return Volume.Isosurface.Loci(volume, props.isoValue);
 }
 
-function getIsosurfaceLoci(pickingId: PickingId, volume: VolumeData, props: VolumeIsosurfaceProps, id: number) {
+function getIsosurfaceLoci(pickingId: PickingId, volume: Volume, props: VolumeIsosurfaceProps, id: number) {
     const { objectId, groupId } = pickingId;
     if (id === objectId) {
         return Volume.Cell.Loci(volume, Interval.ofSingleton(groupId as Volume.CellIndex));
@@ -84,17 +83,17 @@ function getIsosurfaceLoci(pickingId: PickingId, volume: VolumeData, props: Volu
     return EmptyLoci;
 }
 
-function eachIsosurface(loci: Loci, volume: VolumeData, props: VolumeIsosurfaceProps, apply: (interval: Interval) => boolean) {
+function eachIsosurface(loci: Loci, volume: Volume, props: VolumeIsosurfaceProps, apply: (interval: Interval) => boolean) {
     let changed = false;
     if (Volume.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
-        if (apply(Interval.ofLength(volume.data.data.length))) changed = true;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
+        if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
     } else if (Volume.Isosurface.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
-        if (!VolumeIsoValue.areSame(loci.isoValue, props.isoValue, volume.dataStats)) return false;
-        if (apply(Interval.ofLength(volume.data.data.length))) changed = true;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
+        if (!Volume.IsoValue.areSame(loci.isoValue, props.isoValue, volume.grid.stats)) return false;
+        if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
     } else if (Volume.Cell.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
         if (Interval.is(loci.indices)) {
             if (apply(loci.indices)) changed = true;
         } else {
@@ -108,18 +107,18 @@ function eachIsosurface(loci: Loci, volume: VolumeData, props: VolumeIsosurfaceP
 
 //
 
-export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: VolumeData, theme: Theme, props: VolumeIsosurfaceProps, mesh?: Mesh) {
+export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: Volume, theme: Theme, props: VolumeIsosurfaceProps, mesh?: Mesh) {
     ctx.runtime.update({ message: 'Marching cubes...' });
 
-    const ids = fillSerial(new Int32Array(volume.data.data.length));
+    const ids = fillSerial(new Int32Array(volume.grid.cells.data.length));
 
     const surface = await computeMarchingCubesMesh({
-        isoLevel: VolumeIsoValue.toAbsolute(props.isoValue, volume.dataStats).absoluteValue,
-        scalarField: volume.data,
-        idField: Tensor.create(volume.data.space, Tensor.Data1(ids))
+        isoLevel: Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue,
+        scalarField: volume.grid.cells,
+        idField: Tensor.create(volume.grid.cells.space, Tensor.Data1(ids))
     }, mesh).runAsChild(ctx.runtime);
 
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     ctx.runtime.update({ message: 'Transforming mesh...' });
     Mesh.transform(surface, transform);
     return surface;
@@ -136,11 +135,11 @@ export function IsosurfaceMeshVisual(materialId: number): VolumeVisual<Isosurfac
     return VolumeVisual<Mesh, IsosurfaceMeshParams>({
         defaultProps: PD.getDefaultValues(IsosurfaceMeshParams),
         createGeometry: createVolumeIsosurfaceMesh,
-        createLocationIterator: (volume: VolumeData) => LocationIterator(volume.data.data.length, 1, () => NullLocation),
+        createLocationIterator: (volume: Volume) => LocationIterator(volume.grid.cells.data.length, 1, () => NullLocation),
         getLoci: getIsosurfaceLoci,
         eachLocation: eachIsosurface,
-        setUpdateState: (state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<IsosurfaceMeshParams>, currentProps: PD.Values<IsosurfaceMeshParams>) => {
-            if (!VolumeIsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.dataStats)) state.createGeometry = true;
+        setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<IsosurfaceMeshParams>, currentProps: PD.Values<IsosurfaceMeshParams>) => {
+            if (!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats)) state.createGeometry = true;
         },
         geometryUtils: Mesh.Utils
     }, materialId);
@@ -148,18 +147,18 @@ export function IsosurfaceMeshVisual(materialId: number): VolumeVisual<Isosurfac
 
 //
 
-export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume: VolumeData, theme: Theme, props: VolumeIsosurfaceProps, lines?: Lines) {
+export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume: Volume, theme: Theme, props: VolumeIsosurfaceProps, lines?: Lines) {
     ctx.runtime.update({ message: 'Marching cubes...' });
 
-    const ids = fillSerial(new Int32Array(volume.data.data.length));
+    const ids = fillSerial(new Int32Array(volume.grid.cells.data.length));
 
     const wireframe = await computeMarchingCubesLines({
-        isoLevel: VolumeIsoValue.toAbsolute(props.isoValue, volume.dataStats).absoluteValue,
-        scalarField: volume.data,
-        idField: Tensor.create(volume.data.space, Tensor.Data1(ids))
+        isoLevel: Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue,
+        scalarField: volume.grid.cells,
+        idField: Tensor.create(volume.grid.cells.space, Tensor.Data1(ids))
     }, lines).runAsChild(ctx.runtime);
 
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     Lines.transform(wireframe, transform);
 
     return wireframe;
@@ -177,11 +176,11 @@ export function IsosurfaceWireframeVisual(materialId: number): VolumeVisual<Isos
     return VolumeVisual<Lines, IsosurfaceWireframeParams>({
         defaultProps: PD.getDefaultValues(IsosurfaceWireframeParams),
         createGeometry: createVolumeIsosurfaceWireframe,
-        createLocationIterator: (volume: VolumeData) => LocationIterator(volume.data.data.length, 1, () => NullLocation),
+        createLocationIterator: (volume: Volume) => LocationIterator(volume.grid.cells.data.length, 1, () => NullLocation),
         getLoci: getIsosurfaceLoci,
         eachLocation: eachIsosurface,
-        setUpdateState: (state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<IsosurfaceWireframeParams>, currentProps: PD.Values<IsosurfaceWireframeParams>) => {
-            if (!VolumeIsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.dataStats)) state.createGeometry = true;
+        setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<IsosurfaceWireframeParams>, currentProps: PD.Values<IsosurfaceWireframeParams>) => {
+            if (!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats)) state.createGeometry = true;
         },
         geometryUtils: Lines.Utils
     }, materialId);
@@ -190,8 +189,8 @@ export function IsosurfaceWireframeVisual(materialId: number): VolumeVisual<Isos
 //
 
 const IsosurfaceVisuals = {
-    'solid': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceMeshParams>) => VolumeRepresentation('Isosurface mesh', ctx, getParams, IsosurfaceMeshVisual, getLoci),
-    'wireframe': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceWireframeParams>) => VolumeRepresentation('Isosurface wireframe', ctx, getParams, IsosurfaceWireframeVisual, getLoci),
+    'solid': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, IsosurfaceMeshParams>) => VolumeRepresentation('Isosurface mesh', ctx, getParams, IsosurfaceMeshVisual, getLoci),
+    'wireframe': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, IsosurfaceWireframeParams>) => VolumeRepresentation('Isosurface wireframe', ctx, getParams, IsosurfaceWireframeVisual, getLoci),
 };
 
 export const IsosurfaceParams = {
@@ -200,15 +199,15 @@ export const IsosurfaceParams = {
     visuals: PD.MultiSelect(['solid'], PD.objectToOptions(IsosurfaceVisuals)),
 };
 export type IsosurfaceParams = typeof IsosurfaceParams
-export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: VolumeData) {
+export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: Volume) {
     const p = PD.clone(IsosurfaceParams);
-    p.isoValue = createIsoValueParam(VolumeIsoValue.relative(2), volume.dataStats);
+    p.isoValue = createIsoValueParam(Volume.IsoValue.relative(2), volume.grid.stats);
     return p;
 }
 
 export type IsosurfaceRepresentation = VolumeRepresentation<IsosurfaceParams>
-export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceParams>): IsosurfaceRepresentation {
-    return Representation.createMulti('Isosurface', ctx, getParams, Representation.StateBuilder, IsosurfaceVisuals as unknown as Representation.Def<VolumeData, IsosurfaceParams>);
+export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, IsosurfaceParams>): IsosurfaceRepresentation {
+    return Representation.createMulti('Isosurface', ctx, getParams, Representation.StateBuilder, IsosurfaceVisuals as unknown as Representation.Def<Volume, IsosurfaceParams>);
 }
 
 export const IsosurfaceRepresentationProvider = VolumeRepresentationProvider({
@@ -220,5 +219,5 @@ export const IsosurfaceRepresentationProvider = VolumeRepresentationProvider({
     defaultValues: PD.getDefaultValues(IsosurfaceParams),
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
-    isApplicable: (volume: VolumeData) => volume.data.data.length > 0
+    isApplicable: (volume: Volume) => !Volume.isEmpty(volume)
 });
\ No newline at end of file
diff --git a/src/mol-repr/volume/registry.ts b/src/mol-repr/volume/registry.ts
index ee7b0050c6540ddd10f408711e5a8c6d3fa5dbf7..8f952a20cc4733a20eb405b810e7a46a481039bb 100644
--- a/src/mol-repr/volume/registry.ts
+++ b/src/mol-repr/volume/registry.ts
@@ -5,12 +5,12 @@
  */
 
 import { RepresentationRegistry, Representation, RepresentationProvider } from '../representation';
-import { VolumeData } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { IsosurfaceRepresentationProvider } from './isosurface';
 import { objectForEach } from '../../mol-util/object';
 import { SliceRepresentationProvider } from './slice';
 
-export class VolumeRepresentationRegistry extends RepresentationRegistry<VolumeData, Representation.State> {
+export class VolumeRepresentationRegistry extends RepresentationRegistry<Volume, Representation.State> {
     constructor() {
         super();
         objectForEach(VolumeRepresentationRegistry.BuiltIn, (p, k) => {
diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts
index e5f2a7685e357a01ad85289243e99a6c65ba11d2..7e97b98365fcfd42ef3bc2c14e1f711b65b1dc4c 100644
--- a/src/mol-repr/volume/representation.ts
+++ b/src/mol-repr/volume/representation.ts
@@ -6,7 +6,7 @@
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Visual, VisualContext } from '../visual';
-import { VolumeData } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { Geometry, GeometryUtils } from '../../mol-geo/geometry/geometry';
 import { LocationIterator } from '../../mol-geo/util/location-iterator';
 import { Theme } from '../../mol-theme/theme';
@@ -30,9 +30,9 @@ import { Subject } from 'rxjs';
 import { Task } from '../../mol-task';
 import { SizeValues } from '../../mol-gl/renderable/schema';
 
-export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { }
+export interface VolumeVisual<P extends VolumeParams> extends Visual<Volume, P> { }
 
-function createVolumeRenderObject<G extends Geometry>(volume: VolumeData, geometry: G, locationIt: LocationIterator, theme: Theme, props: PD.Values<Geometry.Params<G>>, materialId: number) {
+function createVolumeRenderObject<G extends Geometry>(volume: Volume, geometry: G, locationIt: LocationIterator, theme: Theme, props: PD.Values<Geometry.Params<G>>, materialId: number) {
     const { createValues, createRenderableState } = Geometry.getUtils(geometry);
     const transform = createIdentityTransform();
     const values = createValues(geometry, transform, locationIt, theme, props);
@@ -42,11 +42,11 @@ function createVolumeRenderObject<G extends Geometry>(volume: VolumeData, geomet
 
 interface VolumeVisualBuilder<P extends VolumeParams, G extends Geometry> {
     defaultProps: PD.Values<P>
-    createGeometry(ctx: VisualContext, volume: VolumeData, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G
-    createLocationIterator(volume: VolumeData): LocationIterator
-    getLoci(pickingId: PickingId, volume: VolumeData, props: PD.Values<P>, id: number): Loci
-    eachLocation(loci: Loci, volume: VolumeData, props: PD.Values<P>, apply: (interval: Interval) => boolean): boolean
-    setUpdateState(state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
+    createGeometry(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G
+    createLocationIterator(volume: Volume): LocationIterator
+    getLoci(pickingId: PickingId, volume: Volume, props: PD.Values<P>, id: number): Loci
+    eachLocation(loci: Loci, volume: Volume, props: PD.Values<P>, apply: (interval: Interval) => boolean): boolean
+    setUpdateState(state: VisualUpdateState, volume: Volume, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
 }
 
 interface VolumeVisualGeometryBuilder<P extends VolumeParams, G extends Geometry> extends VolumeVisualBuilder<P, G> {
@@ -62,16 +62,16 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
 
     let newProps: PD.Values<P>;
     let newTheme: Theme;
-    let newVolume: VolumeData;
+    let newVolume: Volume;
 
     let currentProps: PD.Values<P> = Object.assign({}, defaultProps);
     let currentTheme: Theme = Theme.createEmpty();
-    let currentVolume: VolumeData;
+    let currentVolume: Volume;
 
     let geometry: G;
     let locationIt: LocationIterator;
 
-    function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>>, volume: VolumeData) {
+    function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>>, volume: Volume) {
         if (!volume && !currentVolume) {
             throw new Error('missing volume');
         }
@@ -84,7 +84,7 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
 
         if (!renderObject) {
             updateState.createNew = true;
-        } else if (!currentVolume || !VolumeData.areEquivalent(newVolume, currentVolume)) {
+        } else if (!currentVolume || !Volume.areEquivalent(newVolume, currentVolume)) {
             updateState.createNew = true;
         }
 
@@ -158,7 +158,7 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
     return {
         get groupCount() { return locationIt ? locationIt.count : 0; },
         get renderObject () { return renderObject; },
-        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
+        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, volume?: Volume) {
             prepareUpdate(theme, props, volume || currentVolume);
             if (updateState.createGeometry) {
                 const newGeometry = createGeometry(ctx, newVolume, newTheme, newProps, geometry);
@@ -198,9 +198,9 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
     };
 }
 
-export interface VolumeRepresentation<P extends VolumeParams> extends Representation<VolumeData, P> { }
+export interface VolumeRepresentation<P extends VolumeParams> extends Representation<Volume, P> { }
 
-export type VolumeRepresentationProvider<P extends VolumeParams, Id extends string = string> = RepresentationProvider<VolumeData, P, Representation.State, Id>
+export type VolumeRepresentationProvider<P extends VolumeParams, Id extends string = string> = RepresentationProvider<Volume, P, Representation.State, Id>
 export function VolumeRepresentationProvider<P extends VolumeParams, Id extends string>(p: VolumeRepresentationProvider<P, Id>): VolumeRepresentationProvider<P, Id> { return p; }
 
 //
@@ -210,7 +210,7 @@ export const VolumeParams = {
 };
 export type VolumeParams = typeof VolumeParams
 
-export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, P>, visualCtor: (materialId: number) => VolumeVisual<P>, getLoci: (volume: VolumeData, props: PD.Values<P>) => Loci): VolumeRepresentation<P> {
+export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, P>, visualCtor: (materialId: number) => VolumeVisual<P>, getLoci: (volume: Volume, props: PD.Values<P>) => Loci): VolumeRepresentation<P> {
     let version = 0;
     const updated = new Subject<number>();
     const materialId = getNextMaterialId();
@@ -218,12 +218,12 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
     const _state = Representation.createState();
     let visual: VolumeVisual<P> | undefined;
 
-    let _volume: VolumeData;
+    let _volume: Volume;
     let _params: P;
     let _props: PD.Values<P>;
     let _theme = Theme.createEmpty();
 
-    function createOrUpdate(props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
+    function createOrUpdate(props: Partial<PD.Values<P>> = {}, volume?: Volume) {
         if (volume && volume !== _volume) {
             _params = getParams(ctx, volume);
             _volume = volume;
diff --git a/src/mol-repr/volume/slice.ts b/src/mol-repr/volume/slice.ts
index 3b9314597846f81a9abcdf1979eda652059ae50f..485d17d8464e815ea5bf8425d5aacdd4043a6ebd 100644
--- a/src/mol-repr/volume/slice.ts
+++ b/src/mol-repr/volume/slice.ts
@@ -7,14 +7,13 @@
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Image } from '../../mol-geo/geometry/image/image';
 import { ThemeRegistryContext, Theme } from '../../mol-theme/theme';
-import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
+import { Grid, Volume } from '../../mol-model/volume';
 import { VolumeVisual, VolumeRepresentation, VolumeRepresentationProvider } from './representation';
 import { LocationIterator } from '../../mol-geo/util/location-iterator';
 import { VisualUpdateState } from '../util';
 import { NullLocation } from '../../mol-model/location';
 import { RepresentationContext, RepresentationParamsGetter } from '../representation';
 import { VisualContext } from '../visual';
-import { Volume } from '../../mol-model/volume/volume';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci } from '../../mol-model/loci';
 import { Interval, OrderedSet, SortedArray } from '../../mol-data/int';
@@ -25,12 +24,12 @@ import { createIsoValueParam, IsoValueParam } from './isosurface';
 import { Color } from '../../mol-util/color';
 import { ColorTheme } from '../../mol-theme/color';
 
-export async function createImage(ctx: VisualContext, volume: VolumeData, theme: Theme, props: PD.Values<SliceParams>, image?: Image) {
+export async function createImage(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<SliceParams>, image?: Image) {
     const { dimension: { name: dim }, isoValue } = props;
 
-    const { space, data: data } = volume.data;
-    const { min, max } = volume.dataStats;
-    const isoVal = VolumeIsoValue.toAbsolute(isoValue, volume.dataStats).absoluteValue;
+    const { space, data } = volume.grid.cells;
+    const { min, max } = volume.grid.stats;
+    const isoVal = Volume.IsoValue.toAbsolute(isoValue, volume.grid.stats).absoluteValue;
 
     // TODO more color themes
     const color = theme.color.color(NullLocation, false);
@@ -41,7 +40,7 @@ export async function createImage(ctx: VisualContext, volume: VolumeData, theme:
         x, y, z,
         x0, y0, z0,
         nx, ny, nz
-    } = getSliceInfo(volume, props);
+    } = getSliceInfo(volume.grid, props);
 
     const corners = new Float32Array(
         dim === 'x' ? [x, 0, 0,  x, y, 0,  x, 0, z,  x, y, z] :
@@ -50,7 +49,7 @@ export async function createImage(ctx: VisualContext, volume: VolumeData, theme:
     );
 
     const imageArray = new Float32Array(width * height * 4);
-    const groupArray = getGroupArray(volume, props);
+    const groupArray = getGroupArray(volume.grid, props);
 
     let i = 0;
     for (let iy = y0; iy < ny; ++iy) {
@@ -72,15 +71,15 @@ export async function createImage(ctx: VisualContext, volume: VolumeData, theme:
     const imageTexture = { width, height, array: imageArray, flipY: true };
     const groupTexture = { width, height, array: groupArray, flipY: true };
 
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     transformPositionArray(transform, corners, 0, 4);
 
     return Image.create(imageTexture, corners, groupTexture, image);
 }
 
-function getSliceInfo(volume: VolumeData, props: PD.Values<SliceParams>) {
+function getSliceInfo(grid: Grid, props: PD.Values<SliceParams>) {
     const { dimension: { name: dim, params: index } } = props;
-    const { space } = volume.data;
+    const { space } = grid.cells;
 
     let width, height;
     let x, y, z;
@@ -108,9 +107,9 @@ function getSliceInfo(volume: VolumeData, props: PD.Values<SliceParams>) {
     };
 }
 
-function getGroupArray(volume: VolumeData, props: PD.Values<SliceParams>) {
-    const { space } = volume.data;
-    const { width, height, x0, y0, z0, nx, ny, nz } = getSliceInfo(volume, props);
+function getGroupArray(grid: Grid, props: PD.Values<SliceParams>) {
+    const { space } = grid.cells;
+    const { width, height, x0, y0, z0, nx, ny, nz } = getSliceInfo(grid, props);
     const groupArray = new Float32Array(width * height);
 
     let j = 0;
@@ -125,13 +124,13 @@ function getGroupArray(volume: VolumeData, props: PD.Values<SliceParams>) {
     return groupArray;
 }
 
-function getLoci(volume: VolumeData, props: PD.Values<SliceParams>) {
+function getLoci(volume: Volume, props: PD.Values<SliceParams>) {
     // TODO cache somehow?
-    const groupArray = getGroupArray(volume, props);
+    const groupArray = getGroupArray(volume.grid, props);
     return Volume.Cell.Loci(volume, SortedArray.ofUnsortedArray(groupArray));
 }
 
-function getSliceLoci(pickingId: PickingId, volume: VolumeData, props: PD.Values<SliceParams>, id: number) {
+function getSliceLoci(pickingId: PickingId, volume: Volume, props: PD.Values<SliceParams>, id: number) {
     const { objectId, groupId } = pickingId;
     if (id === objectId) {
         return Volume.Cell.Loci(volume, Interval.ofSingleton(groupId as Volume.CellIndex));
@@ -139,24 +138,24 @@ function getSliceLoci(pickingId: PickingId, volume: VolumeData, props: PD.Values
     return EmptyLoci;
 }
 
-function eachSlice(loci: Loci, volume: VolumeData, props: PD.Values<SliceParams>, apply: (interval: Interval) => boolean) {
+function eachSlice(loci: Loci, volume: Volume, props: PD.Values<SliceParams>, apply: (interval: Interval) => boolean) {
     let changed = false;
     if (Volume.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
-        if (apply(Interval.ofLength(volume.data.data.length))) changed = true;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
+        if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
     } else if (Volume.Isosurface.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
         // TODO find a cheaper way?
-        const { dataStats, data: { data } } = volume;
-        const eps = dataStats.sigma;
-        const v = VolumeIsoValue.toAbsolute(loci.isoValue, dataStats).absoluteValue;
+        const { stats, cells: { data } } = volume.grid;
+        const eps = stats.sigma;
+        const v = Volume.IsoValue.toAbsolute(loci.isoValue, stats).absoluteValue;
         for (let i = 0, il = data.length; i < il; ++i) {
             if (equalEps(v, data[i], eps)) {
                 if (apply(Interval.ofSingleton(i))) changed = true;
             }
         }
     } else if (Volume.Cell.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
         if (Interval.is(loci.indices)) {
             if (apply(loci.indices)) changed = true;
         } else {
@@ -181,15 +180,15 @@ export const SliceParams = {
     isoValue: IsoValueParam,
 };
 export type SliceParams = typeof SliceParams
-export function getSliceParams(ctx: ThemeRegistryContext, volume: VolumeData) {
+export function getSliceParams(ctx: ThemeRegistryContext, volume: Volume) {
     const p = PD.clone(SliceParams);
-    const dim = volume.data.space.dimensions;
+    const dim = volume.grid.cells.space.dimensions;
     p.dimension = PD.MappedStatic('x', {
         x: PD.Numeric(0, { min: 0, max: dim[0] - 1, step: 1 }),
         y: PD.Numeric(0, { min: 0, max: dim[1] - 1, step: 1 }),
         z: PD.Numeric(0, { min: 0, max: dim[2] - 1, step: 1 }),
     }, { isEssential: true });
-    p.isoValue = createIsoValueParam(VolumeIsoValue.absolute(volume.dataStats.min), volume.dataStats);
+    p.isoValue = createIsoValueParam(Volume.IsoValue.absolute(volume.grid.stats.min), volume.grid.stats);
     return p;
 }
 
@@ -197,14 +196,14 @@ export function SliceVisual(materialId: number): VolumeVisual<SliceParams> {
     return VolumeVisual<Image, SliceParams>({
         defaultProps: PD.getDefaultValues(SliceParams),
         createGeometry: createImage,
-        createLocationIterator: (volume: VolumeData) => LocationIterator(volume.data.data.length, 1, () => NullLocation),
+        createLocationIterator: (volume: Volume) => LocationIterator(volume.grid.cells.data.length, 1, () => NullLocation),
         getLoci: getSliceLoci,
         eachLocation: eachSlice,
-        setUpdateState: (state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<SliceParams>, currentProps: PD.Values<SliceParams>, newTheme: Theme, currentTheme: Theme) => {
+        setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<SliceParams>, currentProps: PD.Values<SliceParams>, newTheme: Theme, currentTheme: Theme) => {
             state.createGeometry = (
                 newProps.dimension.name !== currentProps.dimension.name ||
                 newProps.dimension.params !== currentProps.dimension.params ||
-                !VolumeIsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.dataStats) ||
+                !Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats) ||
                 !ColorTheme.areEqual(newTheme.color, currentTheme.color)
             );
         },
@@ -226,7 +225,7 @@ function updateRenderableState(state: RenderableState, props: PD.Values<SlicePar
     state.writeDepth = true;
 }
 
-export function SliceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, SliceParams>): VolumeRepresentation<SliceParams> {
+export function SliceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, SliceParams>): VolumeRepresentation<SliceParams> {
     return VolumeRepresentation('Slice', ctx, getParams, SliceVisual, getLoci);
 }
 
@@ -239,5 +238,5 @@ export const SliceRepresentationProvider = VolumeRepresentationProvider({
     defaultValues: PD.getDefaultValues(SliceParams),
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
-    isApplicable: (volume: VolumeData) => volume.data.data.length > 0
+    isApplicable: (volume: Volume) => !Volume.isEmpty(volume)
 });
\ No newline at end of file
diff --git a/src/mol-script/runtime/query/base.ts b/src/mol-script/runtime/query/base.ts
index dabc80ad3deb1efa464997beb2d6dc91ab8e8a92..8f77598a0da57c2e87293fa3eb69121f2bf6a0db 100644
--- a/src/mol-script/runtime/query/base.ts
+++ b/src/mol-script/runtime/query/base.ts
@@ -5,8 +5,9 @@
  */
 
 import Expression from '../../language/expression';
-import { QueryContext, QueryFn, Structure, CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { QueryContext, QueryFn, Structure } from '../../../mol-model/structure';
 import { MSymbol } from '../../language/symbol';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export class QueryRuntimeTable {
     private map = new Map<string, QuerySymbolRuntime>();
diff --git a/src/mol-theme/label.ts b/src/mol-theme/label.ts
index b1d530d8ce4cc6c53abed9961950e646675585dc..2d8cc64cf64a792419d8cad06d67f7c88f655c4b 100644
--- a/src/mol-theme/label.ts
+++ b/src/mol-theme/label.ts
@@ -12,7 +12,7 @@ import { capitalize, stripTags } from '../mol-util/string';
 import { Column } from '../mol-data/db';
 import { Vec3 } from '../mol-math/linear-algebra';
 import { radToDeg } from '../mol-math/misc';
-import { VolumeIsoValue } from '../mol-model/volume';
+import { Volume } from '../mol-model/volume';
 
 export type LabelGranularity = 'element' | 'conformation' | 'residue' | 'chain' | 'structure'
 
@@ -51,19 +51,19 @@ export function lociLabel(loci: Loci, options: Partial<LabelOptions> = {}): stri
         case 'isosurface-loci':
             return [
                 `${loci.volume.label || 'Volume'}`,
-                `Isosurface at ${VolumeIsoValue.toString(loci.isoValue)}`
+                `Isosurface at ${Volume.IsoValue.toString(loci.isoValue)}`
             ].join(' | ');
         case 'cell-loci':
             const size = OrderedSet.size(loci.indices);
             const start = OrderedSet.start(loci.indices);
-            const absVal = VolumeIsoValue.absolute(loci.volume.data.data[start]);
-            const relVal = VolumeIsoValue.toRelative(absVal, loci.volume.dataStats);
+            const absVal = Volume.IsoValue.absolute(loci.volume.grid.cells.data[start]);
+            const relVal = Volume.IsoValue.toRelative(absVal, loci.volume.grid.stats);
             const label = [
                 `${loci.volume.label || 'Volume'}`,
                 `${size === 1 ? `Cell #${start}` : `${size} Cells`}`
             ];
             if (size === 1) {
-                label.push(`${VolumeIsoValue.toString(absVal)} (${VolumeIsoValue.toString(relVal)})`);
+                label.push(`${Volume.IsoValue.toString(absVal)} (${Volume.IsoValue.toString(relVal)})`);
             }
             return label.join(' | ');
     }
diff --git a/src/mol-theme/theme.ts b/src/mol-theme/theme.ts
index 83c4fe8bdf1826bab80d79a07853f6460171a738..2bd1543cdda981926c974bbacda80cc5340ee406 100644
--- a/src/mol-theme/theme.ts
+++ b/src/mol-theme/theme.ts
@@ -7,7 +7,7 @@
 import { ColorTheme } from './color';
 import { SizeTheme } from './size';
 import { Structure } from '../mol-model/structure';
-import { VolumeData } from '../mol-model/volume';
+import { Volume } from '../mol-model/volume';
 import { ParamDefinition as PD } from '../mol-util/param-definition';
 import { Shape } from '../mol-model/shape';
 import { CustomProperty } from '../mol-model-props/common/custom-property';
@@ -21,7 +21,7 @@ export interface ThemeRegistryContext {
 export interface ThemeDataContext {
     [k: string]: any
     structure?: Structure
-    volume?: VolumeData
+    volume?: Volume
     shape?: Shape
 }
 
diff --git a/src/perf-tests/mol-script.ts b/src/perf-tests/mol-script.ts
index eb40e8848ed8c442534e3e89802f263ebb888325..3b4fc50308bd02c3add42349b52764e91e292dff 100644
--- a/src/perf-tests/mol-script.ts
+++ b/src/perf-tests/mol-script.ts
@@ -1,6 +1,6 @@
 import { MolScriptBuilder } from '../mol-script/language/builder';
 import { compile, QuerySymbolRuntime, DefaultQueryRuntimeTable } from '../mol-script/runtime/query/compiler';
-import { QueryContext, Structure, StructureQuery, CustomPropertyDescriptor } from '../mol-model/structure';
+import { QueryContext, Structure, StructureQuery } from '../mol-model/structure';
 import { readCifFile, getModelsAndStructure } from '../apps/structure-info/model';
 import { CustomPropSymbol } from '../mol-script/language/symbol';
 import Type from '../mol-script/language/type';
@@ -10,6 +10,7 @@ import { transpileMolScript } from '../mol-script/script/mol-script/symbols';
 import { formatMolScript } from '../mol-script/language/expression-formatter';
 import { StructureQualityReport, StructureQualityReportProvider } from '../extensions/pdbe/structure-quality-report/prop';
 import fetch from 'node-fetch';
+import { CustomPropertyDescriptor } from '../mol-model/custom-property';
 
 // import Examples from 'mol-script/script/mol-script/examples'
 // import { parseMolScript } from 'mol-script/script/mol-script/parser'