diff --git a/data/mmcif-field-names.csv b/data/mmcif-field-names.csv
index 45602d31ed609b3422c9bff19906fd5ffe8f1ce8..fba153c636ab478f0cdf50ec74a25b7e2245bfa5 100644
--- a/data/mmcif-field-names.csv
+++ b/data/mmcif-field-names.csv
@@ -123,6 +123,17 @@ pdbx_entity_descriptor.type
 pdbx_entity_descriptor.program
 pdbx_entity_descriptor.program_version
 
+pdbx_nonpoly_scheme.asym_id
+pdbx_nonpoly_scheme.entity_id
+pdbx_nonpoly_scheme.mon_id
+pdbx_nonpoly_scheme.ndb_seq_num
+pdbx_nonpoly_scheme.pdb_seq_num
+pdbx_nonpoly_scheme.auth_seq_num
+pdbx_nonpoly_scheme.pdb_mon_id
+pdbx_nonpoly_scheme.auth_mon_id
+pdbx_nonpoly_scheme.pdb_strand_id
+pdbx_nonpoly_scheme.pdb_ins_code
+
 entry.id
 
 exptl.entry_id
diff --git a/docs/model-server/readme.md b/docs/model-server/readme.md
index b4b197c0f04baa2985bd9ded03805c7d00604a8f..02d151c86b4e00c6a6df74844e4b8e966be62325 100644
--- a/docs/model-server/readme.md
+++ b/docs/model-server/readme.md
@@ -14,6 +14,14 @@ npm install
 
 Customize configuration at ``src/server/model/config.ts`` to point to your data and which custom properties to include (see the [Custom Properties](#custom-properties) section). Alternatively, the config can be edited in the compiled version in ``build/node_modules/servers/model/config.js``.
 
+Afterwards, build the project:
+
+```
+npm run build
+```
+
+(or run watch mode for automatic rebuilds: ``npm run watch``)
+
 Running the server locally for testing:
 ```
 npm run model-server
diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts
index b0799e894d3049013f18076b7fe913df6b2df5fd..d2565a478cdb4bb7a9deebaacf3afec883279d59 100644
--- a/src/mol-io/reader/cif/schema/mmcif.ts
+++ b/src/mol-io/reader/cif/schema/mmcif.ts
@@ -1950,6 +1950,18 @@ export const mmCIF_Schema = {
          */
         ordinal: int,
     },
+    pdbx_nonpoly_scheme: {
+        asym_id: str,
+        entity_id: str,
+        mon_id: str,
+        ndb_seq_num: int,
+        pdb_seq_num: int,
+        auth_seq_num: int,
+        pdb_mon_id: str,
+        auth_mon_id: str,
+        pdb_strand_id: str,
+        pdb_ins_code: str
+    },
     /**
      * Data items in the IHM_STARTING_MODEL_DETAILS category records the
      * details about structural models used as starting inputs in
diff --git a/src/mol-math/geometry/gaussian-density.ts b/src/mol-math/geometry/gaussian-density.ts
index b05bb1108e59eb340109b60c2ec2ec585b91fb80..16577af6b9305216a661bd28cf7e917a9913d20d 100644
--- a/src/mol-math/geometry/gaussian-density.ts
+++ b/src/mol-math/geometry/gaussian-density.ts
@@ -8,9 +8,13 @@ import { Box3D } from '../geometry';
 import { Vec3 } from '../linear-algebra';
 import { RuntimeContext, Task } from 'mol-task';
 import { PositionData, DensityData } from './common';
-import { GaussianDensityGPU } from './gaussian-density/gpu';
 import { GaussianDensityCPU } from './gaussian-density/cpu';
 
+// import { GaussianDensityGPU } from './gaussian-density/gpu';
+const GaussianDensityGPU = typeof document !== 'undefined'
+    ? (require('./gaussian-density/gpu') as typeof import('./gaussian-density/gpu')).GaussianDensityGPU
+    : void 0;
+
 export const DefaultGaussianDensityProps = {
     resolution: 1,
     radiusOffset: 0,
@@ -36,6 +40,7 @@ export function computeGaussianDensity(position: PositionData, box: Box3D, radiu
 
 export async function GaussianDensity(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps): Promise<DensityData> {
     if (props.useGpu) {
+        if (!GaussianDensityGPU) throw 'GPU computation not supported on this platform';
         return await GaussianDensityGPU(ctx, position, box, radius, props)
     } else {
         return await GaussianDensityCPU(ctx, position, box, radius, props)
diff --git a/src/mol-model/structure/export/categories/misc.ts b/src/mol-model/structure/export/categories/misc.ts
index 39d3d40f2b1f0202235618e09c81e6634972b8ca..bd4772a45cdb33d7ebc43296dd19b7f69f003967 100644
--- a/src/mol-model/structure/export/categories/misc.ts
+++ b/src/mol-model/structure/export/categories/misc.ts
@@ -32,4 +32,14 @@ export const _pdbx_chem_comp_identifier: CifCategory<CifExportContext> = {
         const indices = Column.indicesOf(comp_id, id => names.has(id));
         return CifCategory.ofTable(pdbx_chem_comp_identifier, indices);
     }
+}
+
+export const _pdbx_nonpoly_scheme: CifCategory<CifExportContext> = {
+    name: 'pdbx_nonpoly_scheme',
+    instance({ firstModel, structures, cache }) {
+        const pdbx_nonpoly_scheme = getModelMmCifCategory(firstModel, 'pdbx_nonpoly_scheme');
+        if (!pdbx_nonpoly_scheme) return CifCategory.Empty;
+        // TODO: filter?
+        return CifCategory.ofTable(pdbx_nonpoly_scheme);
+    }
 }
\ No newline at end of file
diff --git a/src/mol-model/structure/export/categories/utils.ts b/src/mol-model/structure/export/categories/utils.ts
index be0276c94ae628f647ececf34c75e5acf2ac505b..d0a63367f8a4a3c7e472b6feaed0ff32316a4beb 100644
--- a/src/mol-model/structure/export/categories/utils.ts
+++ b/src/mol-model/structure/export/categories/utils.ts
@@ -11,6 +11,8 @@ import { Structure } from '../../structure';
 import { EntityIndex } from '../../model/indexing';
 import { UniqueArray } from 'mol-data/generic';
 import { sortArray } from 'mol-data/util';
+import { CifWriter } from 'mol-io/writer/cif';
+import { CifExportContext } from '../mmcif';
 
 export function getModelMmCifCategory<K extends keyof mmCIF_Schema>(model: Model, name: K): mmCIF_Database[K] | undefined {
     if (model.sourceData.kind !== 'mmCIF') return;
@@ -39,4 +41,20 @@ export function getUniqueEntityIndicesFromStructures(structures: Structure[]): R
     }
     sortArray(ret.array);
     return ret.array;
+}
+
+export function copy_mmCif_category(name: keyof mmCIF_Schema, condition?: (structure: Structure) => boolean): CifWriter.Category<CifExportContext> {
+    return {
+        name,
+        instance({ structures }) {
+            if (condition && !condition(structures[0])) return CifWriter.Category.Empty;
+
+            const model = structures[0].model;
+            if (model.sourceData.kind !== 'mmCIF') return CifWriter.Category.Empty;
+
+            const table = model.sourceData.data[name];
+            if (!table || !table._rowCount) return CifWriter.Category.Empty;
+            return CifWriter.Category.ofTable(table);
+        }
+    };
 }
\ 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 40dd6985cc83b2a7ea4f1d14c7347f93083a917d..e0c189ab4afa7088656e3f0312e2539f7335e4de 100644
--- a/src/mol-model/structure/export/mmcif.ts
+++ b/src/mol-model/structure/export/mmcif.ts
@@ -12,9 +12,9 @@ import { _atom_site } from './categories/atom_site';
 import CifCategory = CifWriter.Category
 import { _struct_conf, _struct_sheet_range } from './categories/secondary-structure';
 import { _pdbx_struct_mod_residue } from './categories/modified-residues';
-import { _chem_comp, _pdbx_chem_comp_identifier } from './categories/misc';
+import { _chem_comp, _pdbx_chem_comp_identifier, _pdbx_nonpoly_scheme } from './categories/misc';
 import { Model } from '../model';
-import { getUniqueEntityIndicesFromStructures } from './categories/utils';
+import { getUniqueEntityIndicesFromStructures, copy_mmCif_category } from './categories/utils';
 import { _struct_asym, _entity_poly, _entity_poly_seq } from './categories/sequence';
 import { ModelPropertyDescriptor } from '../model/properties/custom';
 
@@ -35,19 +35,6 @@ export namespace CifExportContext {
     }
 }
 
-function copy_mmCif_category(name: keyof mmCIF_Schema): CifCategory<CifExportContext> {
-    return {
-        name,
-        instance({ structures }) {
-            const model = structures[0].model;
-            if (model.sourceData.kind !== 'mmCIF') return CifCategory.Empty;
-            const table = model.sourceData.data[name];
-            if (!table || !table._rowCount) return CifCategory.Empty;
-            return CifCategory.ofTable(table);
-        }
-    };
-}
-
 const _entity: CifCategory<CifExportContext> = {
     name: 'entity',
     instance({ structures }) {
@@ -56,6 +43,10 @@ const _entity: CifCategory<CifExportContext> = {
     }
 }
 
+function isWithoutSymmetry(structure: Structure) {
+    return structure.units.every(u => u.conformation.operator.isIdentity)
+}
+
 const Categories = [
     // Basics
     copy_mmCif_category('entry'),
@@ -63,13 +54,13 @@ const Categories = [
     _entity,
 
     // Symmetry
-    copy_mmCif_category('cell'),
-    copy_mmCif_category('symmetry'),
+    copy_mmCif_category('cell', isWithoutSymmetry),
+    copy_mmCif_category('symmetry', isWithoutSymmetry),
 
     // Assemblies
-    copy_mmCif_category('pdbx_struct_assembly'),
-    copy_mmCif_category('pdbx_struct_assembly_gen'),
-    copy_mmCif_category('pdbx_struct_oper_list'),
+    copy_mmCif_category('pdbx_struct_assembly', isWithoutSymmetry),
+    copy_mmCif_category('pdbx_struct_assembly_gen', isWithoutSymmetry),
+    copy_mmCif_category('pdbx_struct_oper_list', isWithoutSymmetry),
 
     // Secondary structure
     _struct_conf,
@@ -91,6 +82,7 @@ const Categories = [
     _pdbx_chem_comp_identifier,
     copy_mmCif_category('atom_sites'),
 
+    _pdbx_nonpoly_scheme,
     _pdbx_struct_mod_residue,
 
     // Atoms
diff --git a/src/servers/model/config.ts b/src/servers/model/config.ts
index 50869b603d5ae0ac191c2d8d593b4d2062ac69f6..a441e5085c03cb44da7aba53a31c2ee9e1a043ea 100644
--- a/src/servers/model/config.ts
+++ b/src/servers/model/config.ts
@@ -81,7 +81,7 @@ const config = {
     mapFile(source: string, id: string) {
         switch (source.toLowerCase()) {
             // case 'pdb': return `e:/test/quick/${id}_updated.cif`;
-            case 'pdb': return `c:/test/mol-star/model/out/${id}_updated.bcif`;
+            case 'pdb': return `e:/test/mol-star/model/out/${id}_updated.bcif`;
             case 'pdb-bcif': return `c:/test/mol-star/model/out/${id}_updated.bcif`;
             case 'pdb-cif': return `c:/test/mol-star/model/out/${id}_updated.cif`;
             default: return void 0;
diff --git a/src/servers/model/query/atoms.ts b/src/servers/model/query/atoms.ts
index 0901023f9e11afe766c3688759b570b2a1b86e6a..4f38ba3d28da5aa2d2530ece44dd0d8484bcd60c 100644
--- a/src/servers/model/query/atoms.ts
+++ b/src/servers/model/query/atoms.ts
@@ -6,7 +6,7 @@
 
 import { QueryPredicate, StructureElement, StructureProperties as Props } from 'mol-model/structure';
 import { AtomsQueryParams } from 'mol-model/structure/query/queries/generators';
-import { AtomSiteSchema } from '../server/api';
+import { AtomSiteSchema, AtomSiteSchemaElement } from '../server/api';
 
 export function getAtomsTests(params: AtomSiteSchema): Partial<AtomsQueryParams>[] {
     if (!params) return [{ }];
@@ -17,7 +17,7 @@ export function getAtomsTests(params: AtomSiteSchema): Partial<AtomsQueryParams>
     }
 }
 
-function atomsTest(params: AtomSiteSchema): Partial<AtomsQueryParams> {
+function atomsTest(params: AtomSiteSchemaElement): Partial<AtomsQueryParams> {
     return {
         entityTest: entityTest(params),
         chainTest: chainTest(params),
@@ -26,13 +26,13 @@ function atomsTest(params: AtomSiteSchema): Partial<AtomsQueryParams> {
     };
 }
 
-function entityTest(params: AtomSiteSchema): QueryPredicate | undefined {
+function entityTest(params: AtomSiteSchemaElement): QueryPredicate | undefined {
     if (!params || typeof params.label_entity_id === 'undefined') return void 0;
     const p = Props.entity.id, id = '' + params.label_entity_id;
     return ctx => p(ctx.element) === id;
 }
 
-function chainTest(params: AtomSiteSchema): QueryPredicate | undefined {
+function chainTest(params: AtomSiteSchemaElement): QueryPredicate | undefined {
     if (!params) return void 0;
 
     if (typeof params.label_asym_id !== 'undefined') {
@@ -46,7 +46,7 @@ function chainTest(params: AtomSiteSchema): QueryPredicate | undefined {
     return void 0;
 }
 
-function residueTest(params: AtomSiteSchema): QueryPredicate | undefined {
+function residueTest(params: AtomSiteSchemaElement): QueryPredicate | undefined {
     if (!params) return void 0;
 
     const props: StructureElement.Property<any>[] = [], values: any[] = [];
@@ -79,7 +79,7 @@ function residueTest(params: AtomSiteSchema): QueryPredicate | undefined {
     return andEqual(props, values);
 }
 
-function atomTest(params: AtomSiteSchema): QueryPredicate | undefined {
+function atomTest(params: AtomSiteSchemaElement): QueryPredicate | undefined {
     if (!params) return void 0;
 
     const props: StructureElement.Property<any>[] = [], values: any[] = [];
diff --git a/src/servers/model/server/api.ts b/src/servers/model/server/api.ts
index ac0e7147cf6e0eb038e0d77aaf2a91b8baf61317..ced5f70163b88405cd57d6bcb47cdf4781a671fc 100644
--- a/src/servers/model/server/api.ts
+++ b/src/servers/model/server/api.ts
@@ -35,7 +35,7 @@ export interface QueryDefinition<Params = any> {
     '@params': Params
 }
 
-export interface AtomSiteSchema {
+export interface AtomSiteSchemaElement {
     label_entity_id?: string,
 
     label_asym_id?: string,
@@ -52,6 +52,8 @@ export interface AtomSiteSchema {
     type_symbol?: string
 }
 
+export type AtomSiteSchema = AtomSiteSchemaElement | AtomSiteSchemaElement[]
+
 const AtomSiteTestParams: QueryParamInfo = {
     name: 'atom_site',
     type: QueryParamType.JSON,
@@ -126,6 +128,16 @@ const QueryMap = {
         },
         params: [ AtomSiteTestParams, RadiusParam ]
     }),
+    'residueSurroundings': Q<{ atom_site: AtomSiteSchema, radius: number }>({
+        niceName: 'Residue Surroundings',
+        description: 'Identifies all residues within the given radius from the source residue.',
+        query(p) {
+            const tests = getAtomsTests(p.atom_site);
+            const center = Queries.combinators.merge(tests.map(test => Queries.generators.atoms(test)));
+            return Queries.modifiers.includeSurroundings(center, { radius: p.radius, wholeResidues: true });
+        },
+        params: [ AtomSiteTestParams, RadiusParam ]
+    })
 };
 
 export type QueryName = keyof typeof QueryMap