Browse Source

Conditional inclusion in CIF writer fields

David Sehnal 6 years ago
parent
commit
f2af614181

+ 12 - 11
src/mol-io/writer/cif/encoder.ts

@@ -21,9 +21,10 @@ import { ArrayEncoder, ArrayEncoding } from '../../common/binary-cif';
 export interface Field<Key = any, Data = any> {
     name: string,
     type: Field.Type,
+    value(key: Key, data: Data): string | number
     valueKind?: (key: Key, data: Data) => Column.ValueKind,
     defaultFormat?: Field.Format,
-    value(key: Key, data: Data): string | number
+    shouldInclude?: (data: Data) => boolean
 }
 
 export namespace Field {
@@ -35,31 +36,31 @@ export namespace Field {
         typedArray?: ArrayEncoding.TypedArrayCtor
     }
 
-    export function getDigitCount(field: Field) {
-        if (field.defaultFormat && typeof field.defaultFormat.digitCount !== 'undefined') return Math.max(0, Math.min(field.defaultFormat.digitCount, 16));
-        return 6;
-    }
+    export type ParamsBase<K, D> = { valueKind?: (k: K, d: D) => Column.ValueKind, encoder?: ArrayEncoder, shouldInclude?: (data: D) => boolean }
 
-    export function str<K, D = any>(name: string, value: (k: K, d: D) => string, params?: { valueKind?: (k: K, d: D) => Column.ValueKind, encoder?: ArrayEncoder }): Field<K, D> {
-        return { name, type: Type.Str, value, valueKind: params && params.valueKind, defaultFormat: params && params.encoder ? { encoder: params.encoder } : void 0 };
+    export function str<K, D = any>(name: string, value: (k: K, d: D) => string, params?: ParamsBase<K, D>): Field<K, D> {
+        return { name, type: Type.Str, value, valueKind: params && params.valueKind, defaultFormat: params && params.encoder ? { encoder: params.encoder } : void 0, shouldInclude: params && params.shouldInclude };
     }
 
-    export function int<K, D = any>(name: string, value: (k: K, d: D) => number, params?: { valueKind?: (k: K, d: D) => Column.ValueKind, encoder?: ArrayEncoder, typedArray?: ArrayEncoding.TypedArrayCtor }): Field<K, D> {
+    export function int<K, D = any>(name: string, value: (k: K, d: D) => number, params?:  ParamsBase<K, D> & { typedArray?: ArrayEncoding.TypedArrayCtor }): Field<K, D> {
         return {
             name,
             type: Type.Int,
             value,
             valueKind: params && params.valueKind,
-            defaultFormat: params ? { encoder: params.encoder, typedArray: params.typedArray } : void 0 };
+            defaultFormat: params ? { encoder: params.encoder, typedArray: params.typedArray } : void 0,
+            shouldInclude: params && params.shouldInclude
+        };
     }
 
-    export function float<K, D = any>(name: string, value: (k: K, d: D) => number, params?: { valueKind?: (k: K, d: D) => Column.ValueKind, encoder?: ArrayEncoder, typedArray?: ArrayEncoding.TypedArrayCtor, digitCount?: number }): Field<K, D> {
+    export function float<K, D = any>(name: string, value: (k: K, d: D) => number, params?: ParamsBase<K, D> & { typedArray?: ArrayEncoding.TypedArrayCtor, digitCount?: number }): Field<K, D> {
         return {
             name,
             type: Type.Float,
             value,
             valueKind: params && params.valueKind,
-            defaultFormat: params ? { encoder: params.encoder, typedArray: params.typedArray, digitCount: typeof params.digitCount !== 'undefined' ? params.digitCount : void 0 } : void 0
+            defaultFormat: params ? { encoder: params.encoder, typedArray: params.typedArray, digitCount: typeof params.digitCount !== 'undefined' ? params.digitCount : void 0 } : void 0,
+            shouldInclude: params && params.shouldInclude
         };
     }
 }

+ 7 - 1
src/mol-io/writer/cif/encoder/binary.ts

@@ -14,6 +14,7 @@ import {
 } from '../../../common/binary-cif'
 import { Field, Category, Encoder } from '../encoder'
 import Writer from '../../writer'
+import { getIncludedFields } from './util';
 
 export default class BinaryEncoder implements Encoder<Uint8Array> {
     private data: EncodedFile;
@@ -57,12 +58,17 @@ export default class BinaryEncoder implements Encoder<Uint8Array> {
         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) }));
-        for (const f of first.fields) {
+        const fields = getIncludedFields(first);
+
+        for (const f of fields) {
             if (!this.filter.includeField(first.name, f.name)) continue;
 
             const format = this.formatter.getFormat(first.name, f.name);
             cat.columns.push(encodeField(f, data, count, getArrayCtor(f, format), getEncoder(f, format)));
         }
+        // no columns included.
+        if (!cat.columns.length) return;
+
         this.dataBlocks[this.dataBlocks.length - 1].categories.push(cat);
     }
 

+ 7 - 3
src/mol-io/writer/cif/encoder/text.ts

@@ -11,6 +11,7 @@ import { Column } from 'mol-data/db'
 import StringBuilder from 'mol-util/string-builder'
 import { Category, Field, Encoder } from '../encoder'
 import Writer from '../../writer'
+import { getFieldDigitCount, getIncludedFields } from './util';
 
 export default class TextEncoder implements Encoder<string> {
     private builder = StringBuilder.create();
@@ -102,13 +103,13 @@ function getFloatPrecisions(categoryName: string, fields: Field[], formatter: Ca
     for (const f of fields) {
         const format = formatter.getFormat(categoryName, f.name);
         if (format && typeof format.digitCount !== 'undefined') ret[ret.length] = f.type === Field.Type.Float ? Math.pow(10, Math.max(0, Math.min(format.digitCount, 15))) : 0;
-        else ret[ret.length] = f.type === Field.Type.Float ? Math.pow(10, Field.getDigitCount(f)) : 0;
+        else ret[ret.length] = f.type === Field.Type.Float ? Math.pow(10, getFieldDigitCount(f)) : 0;
     }
     return ret;
 }
 
 function writeCifSingleRecord(category: Category<any>, builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) {
-    const fields = category.fields;
+    const fields = getIncludedFields(category);
     const data = category.data;
     let width = fields.reduce((w, f) => filter.includeField(category.name, f.name) ? Math.max(w, f.name.length) : 0, 0);
 
@@ -134,8 +135,11 @@ function writeCifSingleRecord(category: Category<any>, builder: StringBuilder, f
 
 function writeCifLoop(categories: Category[], builder: StringBuilder, filter: Category.Filter, formatter: Category.Formatter) {
     const first = categories[0];
-    const fields = filter === Category.DefaultFilter ? first.fields : first.fields.filter(f => filter.includeField(first.name, f.name));
+    const fieldSource = getIncludedFields(first);
+    const fields = filter === Category.DefaultFilter ? fieldSource : fieldSource.filter(f => filter.includeField(first.name, f.name));
     const fieldCount = fields.length;
+    if (fieldCount === 0) return;
+
     const precisions = getFloatPrecisions(first.name, fields, formatter);
 
     writeLine(builder, 'loop_');

+ 18 - 0
src/mol-io/writer/cif/encoder/util.ts

@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Field, Category } from '../encoder';
+
+export function getFieldDigitCount(field: Field) {
+    if (field.defaultFormat && typeof field.defaultFormat.digitCount !== 'undefined') return Math.max(0, Math.min(field.defaultFormat.digitCount, 16));
+    return 6;
+}
+
+export function getIncludedFields(category: Category<any, any>) {
+    return category.fields.some(f => !!f.shouldInclude)
+        ? category.fields.filter(f => !f.shouldInclude || f.shouldInclude(category.data))
+        : category.fields;
+}

+ 1 - 1
src/mol-math/geometry/symmetry-operator.ts

@@ -18,7 +18,7 @@ interface SymmetryOperator {
 }
 
 namespace SymmetryOperator {
-    export const DefaultName = 'identity'
+    export const DefaultName = '1_555'
     export const Default: SymmetryOperator = create(DefaultName, Mat4.identity());
 
     const RotationEpsilon = 0.0001;

+ 4 - 2
src/mol-model/structure/export/mmcif.ts

@@ -47,7 +47,9 @@ const atom_site_fields: CifField<Element.Location>[] = [
     CifField.str('auth_asym_id', P.chain.auth_asym_id),
 
     CifField.int('pdbx_PDB_model_num', P.unit.model_num, { encoder: E.deltaRLE }),
-    CifField.str('operator_name', P.unit.operator_name)
+    CifField.str<Element.Location, Structure>('operator_name', P.unit.operator_name, {
+        shouldInclude: structure => structure.units.some(u => !u.conformation.operator.isIdentity)
+    })
 ];
 
 function copy_mmCif_cat(name: keyof mmCIF_Schema) {
@@ -66,7 +68,7 @@ function _entity({ model, structure }: Context): CifCategory {
 
 function _atom_site({ structure }: Context): CifCategory {
     return {
-        data: void 0,
+        data: structure,
         name: 'atom_site',
         fields: atom_site_fields,
         rowCount: structure.elementCount,