diff --git a/data/mmcif-field-names.csv b/data/mmcif-field-names.csv
index 9b27261b1a36147c031bc7a854bec91b664958f2..1d629c1fa923ee133a80caebaf4dc1e805c82b3d 100644
--- a/data/mmcif-field-names.csv
+++ b/data/mmcif-field-names.csv
@@ -62,6 +62,15 @@ entity.pdbx_mutation
 entity.pdbx_fragment
 entity.pdbx_ec
 
+entity_poly.entity_id
+entity_poly.type
+entity_poly.nstd_linkage
+entity_poly.nstd_monomer
+entity_poly.pdbx_seq_one_letter_code  
+entity_poly.pdbx_seq_one_letter_code_can 
+entity_poly.pdbx_strand_id
+entity_poly.pdbx_target_identifier
+
 entity_poly_seq.entity_id
 entity_poly_seq.num
 entity_poly_seq.mon_id
diff --git a/src/apps/cif2bcif/converter.ts b/src/apps/cif2bcif/converter.ts
index 50ffc0e6e4b901a7d89d25dc1de00a0b3d8af701..d50e9acefdd6d28d79b03c7f582f96c345f8bcd9 100644
--- a/src/apps/cif2bcif/converter.ts
+++ b/src/apps/cif2bcif/converter.ts
@@ -24,15 +24,11 @@ async function getCIF(ctx: RuntimeContext, path: string) {
     return parsed.result;
 }
 
-function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category.Provider {
-    return function (ctx: any) {
-        return {
-            data: cat,
-            name: cat.name,
-            fields,
-            rowCount: cat.rowCount
-        };
-    }
+function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category {
+    return {
+        name: cat.name,
+        instance: () => ({ data: cat, fields, rowCount: cat.rowCount })
+    };
 }
 
 export default function convert(path: string, asText = false) {
diff --git a/src/apps/domain-annotation-server/mapping.ts b/src/apps/domain-annotation-server/mapping.ts
index 581209fe517b69047e139686409d1cbe8f6e881a..1a8c64e01011dc39d5eb307ece2176bc156d7849 100644
--- a/src/apps/domain-annotation-server/mapping.ts
+++ b/src/apps/domain-annotation-server/mapping.ts
@@ -7,7 +7,7 @@
 import { Table } from 'mol-data/db'
 import { CifWriter } from 'mol-io/writer/cif'
 import * as S from './schemas'
-import { getCategoryInstanceProvider } from './utils'
+//import { getCategoryInstanceProvider } from './utils'
 
 export default function create(allData: any) {
     const mols = Object.keys(allData);
@@ -21,7 +21,7 @@ export default function create(allData: any) {
     const sources = getSources(data);
     if (!sources._rowCount) return enc.getData();
 
-    enc.writeCategory(getCategoryInstanceProvider(`pdbx_domain_annotation_sources`, sources));
+    enc.writeCategory({ name: `pdbx_domain_annotation_sources`, instance: () => CifWriter.Category.ofTable(sources) });
 
     for (const cat of Object.keys(S.categories)) {
         writeDomain(enc, getDomain(cat, (S.categories as any)[cat], data));
@@ -38,8 +38,8 @@ type MappingRow = Table.Row<S.mapping>;
 
 function writeDomain(enc: CifWriter.Encoder, domain: DomainAnnotation | undefined) {
     if (!domain) return;
-    enc.writeCategory(getCategoryInstanceProvider(`pdbx_${domain.name}_domain_annotation`, domain.domains));
-    enc.writeCategory(getCategoryInstanceProvider(`pdbx_${domain.name}_domain_mapping`, domain.mappings));
+    enc.writeCategory({ name: `pdbx_${domain.name}_domain_annotation`, instance: () =>  CifWriter.Category.ofTable(domain.domains) });
+    enc.writeCategory({ name: `pdbx_${domain.name}_domain_mapping`, instance: () => CifWriter.Category.ofTable(domain.mappings) });
 }
 
 function getSources(data: any): Table<S.Sources> {
diff --git a/src/apps/domain-annotation-server/utils.ts b/src/apps/domain-annotation-server/utils.ts
deleted file mode 100644
index edd2011674a8f180fe982d91e6c47a1e7e52a160..0000000000000000000000000000000000000000
--- a/src/apps/domain-annotation-server/utils.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { Table } from 'mol-data/db'
-import { CifWriter } from 'mol-io/writer/cif'
-
-function columnValue(k: string) {
-    return (i: number, d: any) => d[k].value(i);
-}
-
-function columnValueKind(k: string) {
-    return (i: number, d: any) => d[k].valueKind(i);
-}
-
-function ofSchema(schema: Table.Schema) {
-    const fields: CifWriter.Field[] = [];
-    for (const k of Object.keys(schema)) {
-        const t = schema[k];
-        const type: any = t.valueType === 'str' ? CifWriter.Field.Type.Str : t.valueType === 'int' ? CifWriter.Field.Type.Int : CifWriter.Field.Type.Float;
-        fields.push({ name: k, type, value: columnValue(k), valueKind: columnValueKind(k) })
-    }
-    return fields;
-}
-
-export function getCategoryInstanceProvider(name: string, table: Table<any>): CifWriter.Category.Provider {
-    return () => {
-        return {
-            data: table,
-            name,
-            fields: ofSchema(table._schema),
-            rowCount: table._rowCount
-        };
-    }
-}
diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts
index 3640bb29ccfacb083ad9b982e2b31d54eea536b2..9cb22fed0a19337c3de02b89930707220af57424 100644
--- a/src/mol-io/reader/cif/schema/mmcif.ts
+++ b/src/mol-io/reader/cif/schema/mmcif.ts
@@ -80,16 +80,26 @@ export const mmCIF_Schema = {
         pdbx_aromatic_flag: Aliased<'Y' | 'N'>(str),
     },
     entity: {
-        details: str,
-        formula_weight: float,
         id: str,
-        src_method: Aliased<'nat' | 'man' | 'syn'>(str),
         type: Aliased<'polymer' | 'non-polymer' | 'macrolide' | 'water'>(str),
+        src_method: Aliased<'nat' | 'man' | 'syn'>(str),
+        formula_weight: float,
         pdbx_description: str,
         pdbx_number_of_molecules: float,
         pdbx_mutation: str,
         pdbx_fragment: str,
         pdbx_ec: List(',', x => x),
+        details: str,
+    },
+    entity_poly: {
+        entity_id: str,
+        type: str,
+        nstd_linkage: Aliased<'no' | 'n' | 'yes' | 'y'>(str),
+        nstd_monomer: Aliased<'no' | 'n' | 'yes' | 'y'>(str),
+        pdbx_seq_one_letter_code: str,
+        pdbx_seq_one_letter_code_can: str,
+        pdbx_strand_id: str,
+        pdbx_target_identifier: str,
     },
     entity_poly_seq: {
         entity_id: str,
@@ -109,11 +119,11 @@ export const mmCIF_Schema = {
         title: str,
     },
     struct_asym: {
-        details: str,
-        entity_id: str,
         id: str,
+        entity_id: str,
         pdbx_modified: str,
         pdbx_blank_PDB_chainid_flag: Aliased<'Y' | 'N'>(str),
+        details: str,
     },
     struct_conf: {
         beg_label_asym_id: str,
diff --git a/src/mol-io/writer/cif/encoder.ts b/src/mol-io/writer/cif/encoder.ts
index 174ea0f8995780f61a2252a5d13dd66ea18a2f69..883f2aed440e97b3ec60ac62f72eff55d2e7e1a3 100644
--- a/src/mol-io/writer/cif/encoder.ts
+++ b/src/mol-io/writer/cif/encoder.ts
@@ -69,19 +69,19 @@ export namespace Field {
     }
 }
 
-export interface Category<Key = any, Data = any> {
+export interface Category<Ctx = any> {
     name: string,
-    fields: Field<Key, Data>[],
-    data?: Data,
-    rowCount: number,
-    keys?: () => Iterator<Key>
+    instance(ctx: Ctx): Category.Instance
 }
 
 export namespace Category {
-    export const Empty: Category = { name: 'empty', rowCount: 0, fields: [] };
+    export const Empty: Instance = { fields: [], rowCount: 0 };
 
-    export interface Provider<Ctx = any> {
-        (ctx: Ctx): Category
+    export interface Instance<Key = any, Data = any> {
+        fields: Field[],
+        data?: Data,
+        rowCount: number,
+        keys?: () => Iterator<Key>
     }
 
     export interface Filter {
@@ -102,11 +102,11 @@ export namespace Category {
         getFormat(cat, field) { return void 0; }
     }
 
-    export function ofTable(name: string, table: Table<Table.Schema>, indices?: ArrayLike<number>): Category<number, Table<Table.Schema>> {
+    export function ofTable(table: Table<Table.Schema>, indices?: ArrayLike<number>): Category.Instance {
         if (indices) {
-            return { name, fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: indices.length, keys: () => Iterator.Array(indices) };
+            return { fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: indices.length, keys: () => Iterator.Array(indices) };
         }
-        return { name, fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: table._rowCount };
+        return { fields: cifFieldsFromTableSchema(table._schema), data: table, rowCount: table._rowCount };
     }
 }
 
@@ -115,7 +115,7 @@ export interface Encoder<T = string | Uint8Array> extends EncoderBase {
     setFormatter(formatter?: Category.Formatter): void,
 
     startDataBlock(header: string): void,
-    writeCategory<Ctx>(category: Category.Provider<Ctx>, contexts?: Ctx[]): void,
+    writeCategory<Ctx>(category: Category<Ctx>, contexts?: Ctx[]): void,
     getData(): T
 }
 
@@ -123,7 +123,7 @@ export namespace Encoder {
     export function writeDatabase(encoder: Encoder, name: string, database: Database<Database.Schema>) {
         encoder.startDataBlock(name);
         for (const table of database._tableNames) {
-            encoder.writeCategory(() => Category.ofTable(table, database[table]));
+            encoder.writeCategory({ name: table, instance: () => Category.ofTable(database[table]) });
         }
     }
 
diff --git a/src/mol-io/writer/cif/encoder/binary.ts b/src/mol-io/writer/cif/encoder/binary.ts
index 01e07965d0295e503d134c3d2adbe19dd44667d5..37b15515faf0e19ffcd5033ba429bad784157fe5 100644
--- a/src/mol-io/writer/cif/encoder/binary.ts
+++ b/src/mol-io/writer/cif/encoder/binary.ts
@@ -38,7 +38,7 @@ export default class BinaryEncoder implements Encoder<Uint8Array> {
         });
     }
 
-    writeCategory<Ctx>(category: Category.Provider<Ctx>, contexts?: Ctx[]) {
+    writeCategory<Ctx>(category: Category<Ctx>, contexts?: Ctx[]) {
         if (!this.data) {
             throw new Error('The writer contents have already been encoded, no more writing.');
         }
@@ -47,23 +47,23 @@ export default class BinaryEncoder implements Encoder<Uint8Array> {
             throw new Error('No data block created.');
         }
 
-        const src = !contexts || !contexts.length ? [category(<any>void 0)] : contexts.map(c => category(c));
-        const categories = src.filter(c => c && c.rowCount > 0);
-        if (!categories.length) return;
-        if (!this.filter.includeCategory(categories[0].name)) return;
+        if (!this.filter.includeCategory(category.name)) return;
 
-        const count = categories.reduce((a, c) => a + c.rowCount, 0);
+        const src = !contexts || !contexts.length ? [category.instance(<any>void 0)] : contexts.map(c => category.instance(c));
+        const instances = src.filter(c => c && c.rowCount > 0);
+        if (!instances.length) return;
+
+        const count = instances.reduce((a, c) => a + c.rowCount, 0);
         if (!count) return;
 
-        const first = categories[0]!;
-        const cat: EncodedCategory = { name: '_' + first.name, columns: [], rowCount: count };
-        const data = categories.map(c => ({ data: c.data, keys: () => c.keys ? c.keys() : Iterator.Range(0, c.rowCount - 1) }));
-        const fields = getIncludedFields(first);
+        const cat: EncodedCategory = { name: '_' + category.name, columns: [], rowCount: count };
+        const data = instances.map(c => ({ data: c.data, keys: () => c.keys ? c.keys() : Iterator.Range(0, c.rowCount - 1) }));
+        const fields = getIncludedFields(instances[0]);
 
         for (const f of fields) {
-            if (!this.filter.includeField(first.name, f.name)) continue;
+            if (!this.filter.includeField(category.name, f.name)) continue;
 
-            const format = this.formatter.getFormat(first.name, f.name);
+            const format = this.formatter.getFormat(category.name, f.name);
             cat.columns.push(encodeField(f, data, count, getArrayCtor(f, format), getEncoder(f, format)));
         }
         // no columns included.
diff --git a/src/mol-io/writer/cif/encoder/text.ts b/src/mol-io/writer/cif/encoder/text.ts
index a191627285c2297171ddaa6e3a1ff31177985b50..6d695bfc9f0605e6591d0ae4bf9e9ebb9413d513 100644
--- a/src/mol-io/writer/cif/encoder/text.ts
+++ b/src/mol-io/writer/cif/encoder/text.ts
@@ -33,7 +33,7 @@ export default class TextEncoder implements Encoder<string> {
         StringBuilder.write(this.builder, `data_${(header || '').replace(/[ \n\t]/g, '').toUpperCase()}\n#\n`);
     }
 
-    writeCategory<Ctx>(category: Category.Provider<Ctx>, contexts?: Ctx[]) {
+    writeCategory<Ctx>(category: Category<Ctx>, contexts?: Ctx[]) {
         if (this.encoded) {
             throw new Error('The writer contents have already been encoded, no more writing.');
         }
@@ -42,18 +42,20 @@ export default class TextEncoder implements Encoder<string> {
             throw new Error('No data block created.');
         }
 
-        const categories = !contexts || !contexts.length ? [category(<any>void 0)] : contexts.map(c => category(c));
-        if (!categories.length) return;
-        if (!this.filter.includeCategory(categories[0].name)) return;
+        if (!this.filter.includeCategory(category.name)) return;
 
-        const rowCount = categories.reduce((v, c) => v + c.rowCount, 0);
+        const src = !contexts || !contexts.length ? [category.instance(<any>void 0)] : contexts.map(c => category.instance(c));
+        const instances = src.filter(c => c && c.rowCount > 0);
+        if (!instances.length) return;
+
+        const rowCount = instances.reduce((v, c) => v + c.rowCount, 0);
 
         if (rowCount === 0) return;
 
         if (rowCount === 1) {
-            writeCifSingleRecord(categories[0]!, this.builder, this.filter, this.formatter);
+            writeCifSingleRecord(category, instances[0]!, this.builder, this.filter, this.formatter);
         } else {
-            writeCifLoop(categories, this.builder, this.filter, this.formatter);
+            writeCifLoop(category, instances, this.builder, this.filter, this.formatter);
         }
     }
 
@@ -108,19 +110,19 @@ function getFloatPrecisions(categoryName: string, fields: Field[], formatter: Ca
     return ret;
 }
 
-function writeCifSingleRecord(category: Category<any>, builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) {
-    const fields = getIncludedFields(category);
-    const data = category.data;
+function writeCifSingleRecord(category: Category, instance: Category.Instance, builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) {
+    const fields = getIncludedFields(instance);
+    const data = instance.data;
     let width = fields.reduce((w, f) => filter.includeField(category.name, f.name) ? Math.max(w, f.name.length) : 0, 0);
 
     // this means no field from this category is included.
     if (width === 0) return;
     width += category.name.length + 6;
 
-    const it = category.keys ? category.keys() : Iterator.Range(0, category.rowCount - 1);
+    const it = instance.keys ? instance.keys() : Iterator.Range(0, instance.rowCount - 1);
     const key = it.move();
 
-    const precisions = getFloatPrecisions(category.name, category.fields, formatter);
+    const precisions = getFloatPrecisions(category.name, instance.fields, formatter);
 
     for (let _f = 0; _f < fields.length; _f++) {
         const f = fields[_f];
@@ -133,28 +135,27 @@ function writeCifSingleRecord(category: Category<any>, builder: StringBuilder, f
     StringBuilder.write(builder, '#\n');
 }
 
-function writeCifLoop(categories: Category[], builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) {
-    const first = categories[0];
-    const fieldSource = getIncludedFields(first);
-    const fields = filter === Category.DefaultFilter ? fieldSource : fieldSource.filter(f => filter.includeField(first.name, f.name));
+function writeCifLoop(category: Category, instances: Category.Instance[], builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) {
+    const fieldSource = getIncludedFields(instances[0]);
+    const fields = filter === Category.DefaultFilter ? fieldSource : fieldSource.filter(f => filter.includeField(category.name, f.name));
     const fieldCount = fields.length;
     if (fieldCount === 0) return;
 
-    const precisions = getFloatPrecisions(first.name, fields, formatter);
+    const precisions = getFloatPrecisions(category.name, fields, formatter);
 
     writeLine(builder, 'loop_');
     for (let i = 0; i < fieldCount; i++) {
-        writeLine(builder, `_${first.name}.${fields[i].name}`);
+        writeLine(builder, `_${category.name}.${fields[i].name}`);
     }
 
     let index = 0;
-    for (let _c = 0; _c < categories.length; _c++) {
-        const category = categories[_c];
-        const data = category.data;
+    for (let _c = 0; _c < instances.length; _c++) {
+        const instance = instances[_c];
+        const data = instance.data;
 
-        if (category.rowCount === 0) continue;
+        if (instance.rowCount === 0) continue;
 
-        const it = category.keys ? category.keys() : Iterator.Range(0, category.rowCount - 1);
+        const it = instance.keys ? instance.keys() : Iterator.Range(0, instance.rowCount - 1);
         while (it.hasNext)  {
             const key = it.move();
 
diff --git a/src/mol-io/writer/cif/encoder/util.ts b/src/mol-io/writer/cif/encoder/util.ts
index af8b3369f61d11939cca26f24b3bae02a0de4a6a..48e80cc4063ac1973eea10e3888478451b0e39ad 100644
--- a/src/mol-io/writer/cif/encoder/util.ts
+++ b/src/mol-io/writer/cif/encoder/util.ts
@@ -11,7 +11,7 @@ export function getFieldDigitCount(field: Field) {
     return 6;
 }
 
-export function getIncludedFields(category: Category<any, any>) {
+export function getIncludedFields(category: Category.Instance) {
     return category.fields.some(f => !!f.shouldInclude)
         ? category.fields.filter(f => !f.shouldInclude || f.shouldInclude(category.data))
         : category.fields;
diff --git a/src/mol-model/structure/export/categories/atom_site.ts b/src/mol-model/structure/export/categories/atom_site.ts
index 0945948f8ecf5951920babdae43ddf5bca7caed6..7e49d127a88174871dde8377d92a1b3f090c6b89 100644
--- a/src/mol-model/structure/export/categories/atom_site.ts
+++ b/src/mol-model/structure/export/categories/atom_site.ts
@@ -39,16 +39,18 @@ const atom_site_fields: CifField<StructureElement>[] = [
 
     CifField.int('pdbx_PDB_model_num', P.unit.model_num, { encoder: E.deltaRLE }),
     CifField.str<StructureElement, Structure>('operator_name', P.unit.operator_name, {
-        shouldInclude: structure => structure.units.some(u => !u.conformation.operator.isIdentity)
+        shouldInclude: structure => { console.log(!!structure); return structure.units.some(u => !u.conformation.operator.isIdentity) }
     })
 ];
 
-export function _atom_site({ structure }: CifExportContext): CifCategory {
-    return {
-        data: structure,
-        name: 'atom_site',
-        fields: atom_site_fields,
-        rowCount: structure.elementCount,
-        keys: () => structure.elementLocations()
+export const _atom_site: CifCategory<CifExportContext> = {
+    name: 'atom_site',
+    instance({ structure }: CifExportContext) {
+        return {
+            fields: atom_site_fields,
+            data: structure,
+            rowCount: structure.elementCount,
+            keys: () => structure.elementLocations()
+        };
     }
 }
\ No newline at end of file
diff --git a/src/mol-model/structure/export/categories/modified-residues.ts b/src/mol-model/structure/export/categories/modified-residues.ts
index 58cdc137fb9daf474b9089c67b3ba9cacb69f7d2..52886186c68ad95c6d22af825227b7a381429094 100644
--- a/src/mol-model/structure/export/categories/modified-residues.ts
+++ b/src/mol-model/structure/export/categories/modified-residues.ts
@@ -13,16 +13,6 @@ import { CifExportContext } from '../mmcif';
 import CifField = CifWriter.Field
 import CifCategory = CifWriter.Category
 
-export function _pdbx_struct_mod_residue(ctx: CifExportContext): CifCategory {
-    const residues = getModifiedResidues(ctx);
-    return {
-        data: residues,
-        name: 'pdbx_struct_mod_residue',
-        fields: pdbx_struct_mod_residue_fields,
-        rowCount: residues.length
-    };
-}
-
 const pdbx_struct_mod_residue_fields: CifField<number, StructureElement[]>[] = [
     CifField.index('id'),
     CifField.str(`label_comp_id`, (i, xs) => P.residue.label_comp_id(xs[i])),
@@ -58,4 +48,12 @@ function getModifiedResidues({ model, structure }: CifExportContext): StructureE
         }
     }
     return ret;
+}
+
+export const _pdbx_struct_mod_residue: CifCategory<CifExportContext> = {
+    name: 'pdbx_struct_mod_residue',
+    instance(ctx) {
+        const residues = getModifiedResidues(ctx);
+        return { fields: pdbx_struct_mod_residue_fields, data: residues, rowCount: residues.length };
+    }
 }
\ No newline at end of file
diff --git a/src/mol-model/structure/export/categories/secondary-structure.ts b/src/mol-model/structure/export/categories/secondary-structure.ts
index aa794fd078d7650ca5180e19076e136d24c85c56..1c18813be27b4607095b9bf0749be9b092658bfe 100644
--- a/src/mol-model/structure/export/categories/secondary-structure.ts
+++ b/src/mol-model/structure/export/categories/secondary-structure.ts
@@ -13,25 +13,21 @@ import CifField = CifWriter.Field
 import CifCategory = CifWriter.Category
 import { Column } from 'mol-data/db';
 
-export function _struct_conf(ctx: CifExportContext): CifCategory {
-    const elements = findElements(ctx, 'helix');
-    return {
-        data: elements,
-        name: 'struct_conf',
-        fields: struct_conf_fields,
-        rowCount: elements.length
-    };
-}
+export const _struct_conf: CifCategory<CifExportContext> = {
+    name: 'struct_conf',
+    instance(ctx) {
+        const elements = findElements(ctx, 'helix');
+        return { fields: struct_conf_fields, data: elements, rowCount: elements.length };
+    }
+};
 
-export function _struct_sheet_range(ctx: CifExportContext): CifCategory {
-    const elements = (findElements(ctx, 'sheet') as SSElement<SecondaryStructure.Sheet>[]).sort(compare_ssr);
-    return {
-        data: elements,
-        name: 'struct_sheet_range',
-        fields: struct_sheet_range_fields,
-        rowCount: elements.length
-    };
-}
+export const _struct_sheet_range: CifCategory<CifExportContext> = {
+    name: 'struct_sheet_range',
+    instance(ctx) {
+        const elements = (findElements(ctx, 'sheet') as SSElement<SecondaryStructure.Sheet>[]).sort(compare_ssr);
+        return { fields: struct_sheet_range_fields, data: elements, rowCount: elements.length };
+    }
+};
 
 function compare_ssr(x: SSElement<SecondaryStructure.Sheet>, y: SSElement<SecondaryStructure.Sheet>) {
     const a = x.element, b = y.element;
diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts
index 89d18e9b1963eebf2cf0121db230eca462d4056f..f8c9d7dcb661acc99d891efe430faeddf59c8b48 100644
--- a/src/mol-model/structure/export/mmcif.ts
+++ b/src/mol-model/structure/export/mmcif.ts
@@ -10,28 +10,34 @@ import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'
 import { Structure } from '../structure'
 import { Model } from '../model'
 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';
 
 export interface CifExportContext {
     structure: Structure,
-    model: Model
+    model: Model,
+    cache: any
 }
 
-import CifCategory = CifWriter.Category
-import { _struct_conf, _struct_sheet_range } from './categories/secondary-structure';
-import { _pdbx_struct_mod_residue } from './categories/modified-residues';
-
-function copy_mmCif_category(name: keyof mmCIF_Schema) {
-    return ({ model }: CifExportContext) => {
-        if (model.sourceData.kind !== 'mmCIF') return CifCategory.Empty;
-        const table = model.sourceData.data[name];
-        if (!table || !table._rowCount) return CifCategory.Empty;
-        return CifCategory.ofTable(name, table);
+function copy_mmCif_category(name: keyof mmCIF_Schema): CifCategory<CifExportContext> {
+    return {
+        name,
+        instance({ 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);
+        }
     };
 }
 
-function _entity({ model, structure }: CifExportContext): CifCategory {
-    const keys = Structure.getEntityKeys(structure);
-    return CifCategory.ofTable('entity', model.entities.data, keys);
+const _entity: CifCategory<CifExportContext> = {
+    name: 'entity',
+    instance({ structure, model}) {
+        const keys = Structure.getEntityKeys(structure);
+        return CifCategory.ofTable(model.entities.data, keys);
+    }
 }
 
 const Categories = [
@@ -53,6 +59,11 @@ const Categories = [
     _struct_conf,
     _struct_sheet_range,
 
+    // Sequence
+    copy_mmCif_category('struct_asym'), // TODO: filter only present chains?
+    copy_mmCif_category('entity_poly'),
+    copy_mmCif_category('entity_poly_seq'),
+
     // Misc
     // TODO: filter for actual present residues?
     copy_mmCif_category('chem_comp'),
@@ -81,13 +92,13 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: S
     if (models.length !== 1) throw 'Can\'t export stucture composed from multiple models.';
     const model = models[0];
 
-    const ctx: CifExportContext[] = [{ structure, model }];
+    const ctx: CifExportContext[] = [{ structure, model, cache: Object.create(null) }];
 
     for (const cat of Categories) {
         encoder.writeCategory(cat, ctx);
     }
     for (const customProp of model.customProperties.all) {
-        const cats = customProp.cifExport.categoryProvider(ctx[0]);
+        const cats = customProp.cifExport.categories;
         for (const cat of cats) {
             encoder.writeCategory(cat, ctx);
         }
diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts
index 1cdbcb6be294603128c847379e1c885ae60ad6ee..2785d960372256cf9352a9946d4e3228ec87012b 100644
--- a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts
+++ b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts
@@ -22,22 +22,22 @@ export namespace ComponentBond {
         isStatic: true,
         name: 'chem_comp_bond',
         cifExport: {
-            categoryNames: ['chem_comp_bond'],
-            categoryProvider(ctx) {
-                const chem_comp_bond = getChemCompBond(ctx.model);
-                if (!chem_comp_bond) return [];
-
-                const comp_names = getUniqueResidueNames(ctx.structure);
-                const { comp_id, _rowCount } = chem_comp_bond;
-                const indices: number[] = [];
-                for (let i = 0; i < _rowCount; i++) {
-                    if (comp_names.has(comp_id.value(i))) indices[indices.length] = i;
+            categories: [{
+                name: 'chem_comp_bond',
+                instance(ctx) {
+                    const chem_comp_bond = getChemCompBond(ctx.model);
+                    if (!chem_comp_bond) return CifWriter.Category.Empty;
+
+                    const comp_names = getUniqueResidueNames(ctx.structure);
+                    const { comp_id, _rowCount } = chem_comp_bond;
+                    const indices: number[] = [];
+                    for (let i = 0; i < _rowCount; i++) {
+                        if (comp_names.has(comp_id.value(i))) indices[indices.length] = i;
+                    }
+
+                    return CifWriter.Category.ofTable(chem_comp_bond, indices)
                 }
-
-                return [
-                    () => CifWriter.Category.ofTable('chem_comp_bond', chem_comp_bond, indices)
-                ];
-            }
+            }]
         }
     }
 
diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts b/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts
index 0d14fd75c581a37af39726f307fbec89219a1107..933705171012573a68167af9657cfa80e7460fd1 100644
--- a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts
+++ b/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts
@@ -27,38 +27,38 @@ export namespace StructConn {
         isStatic: true,
         name: 'struct_conn',
         cifExport: {
-            categoryNames: ['struct_conn'],
-            categoryProvider(ctx) {
-                const struct_conn = getStructConn(ctx.model);
-                if (!struct_conn) return [];
+            categories: [{
+                name: 'struct_conn',
+                instance(ctx) {
+                    const struct_conn = getStructConn(ctx.model);
+                    if (!struct_conn) return CifWriter.Category.Empty;
 
-                const strConn = get(ctx.model);
-                if (!strConn || strConn.entries.length === 0) return [];
+                    const strConn = get(ctx.model);
+                    if (!strConn || strConn.entries.length === 0) return CifWriter.Category.Empty;
 
-                const foundAtoms = new Set<ElementIndex>();
-                const indices: number[] = [];
-                for (const entry of strConn.entries) {
-                    const { partners } = entry;
-                    let hasAll = true;
-                    for (let i = 0, _i = partners.length; i < _i; i++) {
-                        const atom = partners[i].atomIndex;
-                        if (foundAtoms.has(atom)) continue;
-                        if (hasAtom(ctx.structure, atom)) {
-                            foundAtoms.add(atom);
-                        } else {
-                            hasAll = false;
-                            break;
+                    const foundAtoms = new Set<ElementIndex>();
+                    const indices: number[] = [];
+                    for (const entry of strConn.entries) {
+                        const { partners } = entry;
+                        let hasAll = true;
+                        for (let i = 0, _i = partners.length; i < _i; i++) {
+                            const atom = partners[i].atomIndex;
+                            if (foundAtoms.has(atom)) continue;
+                            if (hasAtom(ctx.structure, atom)) {
+                                foundAtoms.add(atom);
+                            } else {
+                                hasAll = false;
+                                break;
+                            }
+                        }
+                        if (hasAll) {
+                            indices[indices.length] = entry.rowIndex;
                         }
                     }
-                    if (hasAll) {
-                        indices[indices.length] = entry.rowIndex;
-                    }
-                }
 
-                return [
-                    () => CifWriter.Category.ofTable('struct_conn', struct_conn, indices)
-                ];
-            }
+                    return CifWriter.Category.ofTable(struct_conn, indices);
+                }
+            }]
         }
     }
 
diff --git a/src/mol-model/structure/model/properties/custom/descriptor.ts b/src/mol-model/structure/model/properties/custom/descriptor.ts
index 89304d1972e5163eb24b03b44cd925de79a789dd..d86dd94a6fa415db073a466c0493b776854bd575 100644
--- a/src/mol-model/structure/model/properties/custom/descriptor.ts
+++ b/src/mol-model/structure/model/properties/custom/descriptor.ts
@@ -12,9 +12,7 @@ interface ModelPropertyDescriptor {
     readonly name: string,
 
     cifExport: {
-        /** used category names that can be used for "filtering" by the writer */
-        readonly categoryNames: ReadonlyArray<string>,
-        categoryProvider: (ctx: CifExportContext) => CifWriter.Category.Provider[]
+        categories: CifWriter.Category<CifExportContext>[]
     }
 }
 
diff --git a/src/perf-tests/cif-encoder.ts b/src/perf-tests/cif-encoder.ts
index 2d07b1890fc145d7f5d185057d19d21bc94227a5..f8f2ab18c9bb925ae7ba1b46dd5c83b1ccd564ef 100644
--- a/src/perf-tests/cif-encoder.ts
+++ b/src/perf-tests/cif-encoder.ts
@@ -5,6 +5,7 @@
  */
 
 import { CifWriter } from 'mol-io/writer/cif'
+import * as fs from 'fs'
 
 const category1fields: CifWriter.Field[] = [
     CifWriter.Field.str('f1', i => 'v' + i),
@@ -18,24 +19,49 @@ const category2fields: CifWriter.Field[] = [
     CifWriter.Field.float('e3', i => Math.random()),
 ];
 
-function getInstance(ctx: { name: string, fields: CifWriter.Field[], rowCount: number }): CifWriter.Category {
+function getCat(name: string): CifWriter.Category {
     return {
-        data: void 0,
-        name: ctx.name,
-        fields: ctx.fields,
-        rowCount: ctx.rowCount
+        name,
+        instance(ctx: { fields: CifWriter.Field[], rowCount: number }) {
+            return { data: void 0, fields: ctx.fields, rowCount: ctx.rowCount };
+        }
+    };
+}
+
+function testText() {
+    const enc = CifWriter.createEncoder();
+
+    const filter: CifWriter.Category.Filter = {
+        includeCategory(cat) { return true; },
+        includeField(cat, field) { return !(cat === 'cat2' && field === 'e2') }
     }
+
+    enc.startDataBlock('test');
+    enc.setFilter(filter);
+    enc.writeCategory(getCat('cat1'), [{ rowCount: 5, fields: category1fields }]);
+    enc.writeCategory(getCat('cat2'), [{ rowCount: 1, fields: category2fields }]);
+    console.log(enc.getData());
 }
 
-const enc = CifWriter.createEncoder();
+testText();
+
+
+function testBinary() {
+    const enc = CifWriter.createEncoder({ binary: true });
+
+    const filter: CifWriter.Category.Filter = {
+        includeCategory(cat) { return true; },
+        includeField(cat, field) { return !(cat === 'cat2' && field === 'e2') }
+    }
 
-const filter: CifWriter.Category.Filter = {
-    includeCategory(cat) { return true; },
-    includeField(cat, field) { return !(cat === 'cat2' && field === 'e2') }
+    enc.startDataBlock('test');
+    enc.setFilter(filter);
+    enc.writeCategory(getCat('cat1'), [{ rowCount: 5, fields: category1fields }]);
+    enc.writeCategory(getCat('cat2'), [{ rowCount: 1, fields: category2fields }]);
+    enc.encode();
+    const data = enc.getData() as Uint8Array;
+    fs.writeFileSync('e:/test/mol-star/test.bcif', new Buffer(data));
+    console.log('written binary');
 }
 
-enc.startDataBlock('test');
-enc.setFilter(filter);
-enc.writeCategory(getInstance, [{ rowCount: 5, name: 'cat1', fields: category1fields }]);
-enc.writeCategory(getInstance, [{ rowCount: 1, name: 'cat2', fields: category2fields  }]);
-console.log(enc.getData());
+testBinary();
\ No newline at end of file
diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts
index ed9ebedf88f1901edca307a812b4df045fada995..2dc635c4ee79bdeaa7735533615cd7a49a7e2f5d 100644
--- a/src/servers/model/server/query.ts
+++ b/src/servers/model/server/query.ts
@@ -95,6 +95,7 @@ export async function resolveRequest(req: Request, writer: Writer) {
     };
 
     encoder.writeCategory(_model_server_stats, [stats]);
+    encoder.encode();
 
     encoder.writeTo(writer);
 
@@ -112,9 +113,9 @@ import CifField = CifWriter.Field
 
 function string<T>(name: string, str: (data: T, i: number) => string, isSpecified?: (data: T) => boolean): CifField<number, T> {
     if (isSpecified) {
-        return CifField.str(name,  (i, d) => str(d, i), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent });
+        return CifField.str(name, (i, d) => str(d, i), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent });
     }
-    return CifField.str(name,  (i, d) => str(d, i));
+    return CifField.str(name, (i, d) => str(d, i));
 }
 
 function int32<T>(name: string, value: (data: T) => number): CifField<number, T> {
@@ -144,33 +145,29 @@ const _model_server_stats_fields: CifField<number, Stats>[] = [
 ];
 
 
-function _model_server_result(request: Request): CifWriter.Category {
-    return {
-        data: request,
-        name: 'model_server_result',
-        fields: _model_server_result_fields,
-        rowCount: 1
-    };
-}
-
-function _model_server_params(request: Request): CifWriter.Category {
-    const params: string[][] = [];
-    for (const k of Object.keys(request.normalizedParams)) {
-        params.push([k, '' + request.normalizedParams[k]]);
+const _model_server_result: CifWriter.Category<Request> = {
+    name: 'model_server_result',
+    instance: (request) => ({ data: request, fields: _model_server_result_fields, rowCount: 1 })
+};
+
+const _model_server_params: CifWriter.Category<Request> = {
+    name: 'model_server_params',
+    instance(request) {
+        const params: string[][] = [];
+        for (const k of Object.keys(request.normalizedParams)) {
+            params.push([k, '' + request.normalizedParams[k]]);
+        }
+        return {
+            data: params,
+
+            fields: _model_server_params_fields,
+            rowCount: params.length
+        }
     }
-    return {
-        data: params,
-        name: 'model_server_params',
-        fields: _model_server_params_fields,
-        rowCount: params.length
-    };
-}
+};
 
-function _model_server_stats(stats: Stats): CifWriter.Category {
-    return {
-        data: stats,
-        name: 'model_server_stats',
-        fields: _model_server_stats_fields,
-        rowCount: 1
-    };
+
+const _model_server_stats: CifWriter.Category<Stats> = {
+    name: 'model_server_stats',
+    instance: (stats) => ({ data: stats, fields: _model_server_stats_fields, rowCount: 1 })
 }
\ No newline at end of file
diff --git a/src/servers/volume/server/query/encode.ts b/src/servers/volume/server/query/encode.ts
index c0cbab224c18bb56fca276cdd4412caab18bafb2..a754ef489fbe3d5eea0c1ff1869f7c52c38d4eec 100644
--- a/src/servers/volume/server/query/encode.ts
+++ b/src/servers/volume/server/query/encode.ts
@@ -28,9 +28,9 @@ interface ResultContext {
 
 function string<T>(name: string, str: (data: T) => string, isSpecified?: (data: T) => boolean): CifWriter.Field<number, T> {
     if (isSpecified) {
-        return CifWriter.Field.str(name,  (i, d) => str(d), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent });
+        return CifWriter.Field.str(name, (i, d) => str(d), { valueKind: (i, d) => isSpecified(d) ? Column.ValueKind.Present : Column.ValueKind.NotPresent });
     }
-    return CifWriter.Field.str(name,  (i, d) => str(d));
+    return CifWriter.Field.str(name, (i, d) => str(d));
 }
 
 function int32<T>(name: string, value: (data: T) => number): CifWriter.Field<number, T> {
@@ -90,53 +90,54 @@ const _volume_data_3d_info_fields = [
     float64<_vd3d_Ctx>('max_sampled', ctx => ctx.sampledValuesInfo.max)
 ];
 
-function _volume_data_3d_info(result: ResultContext): CifWriter.Category {
-    const ctx: _vd3d_Ctx = {
-        header: result.query.data.header,
-        channelIndex: result.channelIndex,
-        grid: result.query.samplingInfo.gridDomain,
-        sampleRate: result.query.samplingInfo.sampling.rate,
-        globalValuesInfo: result.query.data.header.sampling[0].valuesInfo[result.channelIndex],
-        sampledValuesInfo: result.query.data.header.sampling[result.query.samplingInfo.sampling.index].valuesInfo[result.channelIndex]
-    };
-
-    return {
-        data: ctx,
-        name: 'volume_data_3d_info',
-        fields: _volume_data_3d_info_fields,
-        rowCount: 1
-    };
-}
+const _volume_data_3d_info: CifWriter.Category<ResultContext> = {
+    name: 'volume_data_3d_info',
+    instance(result) {
+        const ctx: _vd3d_Ctx = {
+            header: result.query.data.header,
+            channelIndex: result.channelIndex,
+            grid: result.query.samplingInfo.gridDomain,
+            sampleRate: result.query.samplingInfo.sampling.rate,
+            globalValuesInfo: result.query.data.header.sampling[0].valuesInfo[result.channelIndex],
+            sampledValuesInfo: result.query.data.header.sampling[result.query.samplingInfo.sampling.index].valuesInfo[result.channelIndex]
+        };
+
+        return { data: ctx, fields: _volume_data_3d_info_fields, rowCount: 1 }
+    }
+};
 
 function _volume_data_3d_number(i: number, ctx: DataFormat.ValueArray): number {
     return ctx[i];
 }
 
-function _volume_data_3d(ctx: ResultContext) {
-    const data = ctx.query.values[ctx.channelIndex];
-
-    const E = ArrayEncoding;
-    let encoder: ArrayEncoder;
-    let typedArray: any;
-    if (ctx.query.data.header.valueType === DataFormat.ValueType.Float32 || ctx.query.data.header.valueType === DataFormat.ValueType.Int16) {
-        let min: number, max: number;
-        min = data[0], max = data[0];
-        for (let i = 0, n = data.length; i < n; i++) {
-            let v = data[i];
-            if (v < min) min = v;
-            else if (v > max) max = v;
+const _volume_data_3d: CifWriter.Category<ResultContext> = {
+    name: 'volume_data_3d',
+    instance(ctx) {
+        const data = ctx.query.values[ctx.channelIndex];
+
+        const E = ArrayEncoding;
+        let encoder: ArrayEncoder;
+        let typedArray: any;
+        if (ctx.query.data.header.valueType === DataFormat.ValueType.Float32 || ctx.query.data.header.valueType === DataFormat.ValueType.Int16) {
+            let min: number, max: number;
+            min = data[0], max = data[0];
+            for (let i = 0, n = data.length; i < n; i++) {
+                let v = data[i];
+                if (v < min) min = v;
+                else if (v > max) max = v;
+            }
+            typedArray = Float32Array;
+            // encode into 255 steps and store each value in 1 byte.
+            encoder = E.by(E.intervalQuantizaiton(min, max, 255, Uint8Array)).and(E.byteArray);
+        } else {
+            typedArray = Int8Array;
+            // just encode the bytes
+            encoder = E.by(E.byteArray)
         }
-        typedArray = Float32Array;
-        // encode into 255 steps and store each value in 1 byte.
-        encoder = E.by(E.intervalQuantizaiton(min, max, 255, Uint8Array)).and(E.byteArray);
-    } else {
-        typedArray = Int8Array;
-        // just encode the bytes
-        encoder = E.by(E.byteArray)
-    }
 
-    const fields = [CifWriter.Field.float('values', _volume_data_3d_number, { encoder, typedArray, digitCount: 6 })];
-    return { data, name: 'volume_data_3d', fields, rowCount: data.length };
+        const fields = [CifWriter.Field.float('values', _volume_data_3d_number, { encoder, typedArray, digitCount: 6 })];
+        return { data, fields, rowCount: data.length };
+    }
 }
 
 function pickQueryBoxDimension(ctx: Data.QueryContext, e: 'a' | 'b', d: number) {
@@ -171,13 +172,9 @@ const _density_server_result_fields = [
     queryBoxDimension('b', 2)
 ]
 
-function _density_server_result(ctx: Data.QueryContext): CifWriter.Category {
-    return {
-        data: ctx,
-        name: 'density_server_result',
-        fields: _density_server_result_fields,
-        rowCount: 1
-    };
+const _density_server_result: CifWriter.Category<Data.QueryContext> = {
+    name: 'density_server_result',
+    instance: ctx => ({ data: ctx, fields: _density_server_result_fields, rowCount: 1 })
 }
 
 function write(encoder: CifWriter.Encoder, query: Data.QueryContext) {