Browse Source

wip: multiple models export to CIF

David Sehnal 6 years ago
parent
commit
e6e97c48be

+ 5 - 2
src/mol-model-props/pdbe/structure-quality-report.ts

@@ -5,7 +5,7 @@
  */
 
 import { CifWriter } from 'mol-io/writer/cif';
-import { Model, ModelPropertyDescriptor, ResidueIndex, Unit, ResidueCustomProperty } from 'mol-model/structure';
+import { Model, ModelPropertyDescriptor, ResidueIndex, Unit, ResidueCustomProperty, StructureProperties as P } from 'mol-model/structure';
 import { residueIdFields } from 'mol-model/structure/export/categories/atom_site';
 import CifField = CifWriter.Field;
 import { mmCIF_residueId_schema } from 'mol-io/reader/cif/schema/mmcif-extras';
@@ -50,6 +50,7 @@ type ExportCtx = ResidueCustomProperty.ExportCtx<string[]>
 const _structure_quality_report_issues_fields: CifField<number, ExportCtx>[] = [
     CifField.index('id'),
     ...residueIdFields<number, ExportCtx>((i, d) => d.elements[i]),
+    CifField.int<number, ExportCtx>('pdbx_PDB_model_num', (i, d) => P.unit.model_num(d.elements[i])),
     CifField.str<number, ExportCtx>('issues', (i, d) => d.property(i).join(','))
 ];
 
@@ -83,9 +84,10 @@ function createIssueMapFromJson(modelData: Model, data: any): IssueMap | undefin
 
 function createIssueMapFromCif(modelData: Model, data: Table<typeof StructureQualityReport.Schema.pdbe_structure_quality_report_issues>): IssueMap | undefined {
     const ret = new Map<ResidueIndex, string[]>();
-    const { label_entity_id, label_asym_id, auth_seq_id, pdbx_PDB_ins_code, issues, _rowCount } = data;
+    const { label_entity_id, label_asym_id, auth_seq_id, pdbx_PDB_ins_code, issues, pdbx_PDB_model_num, _rowCount } = data;
 
     for (let i = 0; i < _rowCount; i++) {
+        if (pdbx_PDB_model_num.value(i) !== modelData.modelNum) continue;
         const idx = modelData.atomicHierarchy.index.findResidue(label_entity_id.value(i), label_asym_id.value(i), auth_seq_id.value(i), pdbx_PDB_ins_code.value(i));
         ret.set(idx, issues.value(i));
     }
@@ -103,6 +105,7 @@ export namespace StructureQualityReport {
         pdbe_structure_quality_report_issues: {
             id: Column.Schema.int,
             ...mmCIF_residueId_schema,
+            pdbx_PDB_model_num: Column.Schema.int,
             issues: Column.Schema.List(',', x => x)
         }
     }

+ 9 - 8
src/mol-model/structure/export/mmcif.ts

@@ -21,8 +21,9 @@ export interface CifExportContext {
 }
 
 export namespace CifExportContext {
-    export function create(structure: Structure, model: Model): CifExportContext {
-        return { structure, model, cache: Object.create(null) };
+    export function create(structures: Structure | Structure[]): CifExportContext[] {
+        if (Array.isArray(structures)) return structures.map(structure => ({ structure, model: structure.models[0], cache: Object.create(null) }));
+        return [{ structure: structures, model: structures.models[0], cache: Object.create(null) }];
     }
 }
 
@@ -99,20 +100,20 @@ export const mmCIF_Export_Filters = {
 }
 
 /** Doesn't start a data block */
-export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: Structure, params?: { skipCategoryNames?: Set<string>, exportCtx?: CifExportContext }) {
-    const models = structure.models;
+export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures: Structure | Structure[], params?: { skipCategoryNames?: Set<string>, exportCtx?: CifExportContext[] }) {
+    const first = Array.isArray(structures) ? structures[0] : (structures as Structure);
+    const models = first.models;
     if (models.length !== 1) throw 'Can\'t export stucture composed from multiple models.';
-    const model = models[0];
 
     const _params = params || { };
-
-    const ctx: CifExportContext[] = [_params.exportCtx ? _params.exportCtx : CifExportContext.create(structure, model)];
+    const ctx: CifExportContext[] = params && params.exportCtx ? params.exportCtx : CifExportContext.create(structures);
 
     for (const cat of Categories) {
         if (_params.skipCategoryNames && _params.skipCategoryNames.has(cat.name)) continue;
         encoder.writeCategory(cat, ctx);
     }
-    for (const customProp of model.customProperties.all) {
+
+    for (const customProp of models[0].customProperties.all) {
         if (!customProp.cifExport || customProp.cifExport.categories.length === 0) continue;
 
         const prefix = customProp.cifExport.prefix;

+ 3 - 3
src/servers/model/preprocess/preprocess.ts

@@ -25,12 +25,12 @@ export async function preprocessFile(filename: string, outputCif?: string, outpu
     //ConsoleLogger.log(`${linearId}`, `Reading '${filename}'...`);
     // TODO: support the custom prop provider list here.
     const input = await readStructureWrapper('entry', '_local_', filename, void 0);
+    const categories = await classifyCif(input.cifFrame);
     const inputStructure = (await resolveStructure(input))!;
     //ConsoleLogger.log(`${linearId}`, `Classifying CIF categories...`);
-    const categories = await classifyCif(input.cifFrame);
     //clearLine();
 
-    const exportCtx = CifExportContext.create(inputStructure, inputStructure.models[0]);
+    const exportCtx = CifExportContext.create(inputStructure);
 
     if (outputCif) {
         //ConsoleLogger.log(`${linearId}`, `Encoding CIF...`);
@@ -52,7 +52,7 @@ export async function preprocessFile(filename: string, outputCif?: string, outpu
     // ConsoleLogger.log(`${linearId}`, `Finished '${filename}' in ${Math.round(now() - started)}ms`);
 }
 
-function encode(structure: Structure, header: string, categories: CifWriter.Category[], encoder: CifWriter.Encoder, exportCtx: CifExportContext, writer: Writer) {
+function encode(structure: Structure, header: string, categories: CifWriter.Category[], encoder: CifWriter.Encoder, exportCtx: CifExportContext[], writer: Writer) {
     return Task.create('Encode', async ctx => {
         const skipCategoryNames = new Set<string>(categories.map(c => c.name));
         encoder.startDataBlock(header);

+ 0 - 0
src/servers/model/provider.ts → src/servers/model/property-provider.ts


+ 3 - 3
src/servers/model/server/query.ts

@@ -16,7 +16,7 @@ import Version from '../version';
 import { Job } from './jobs';
 import { createStructureWrapperFromJob, StructureWrapper, resolveStructure } from './structure-wrapper';
 import CifField = CifWriter.Field
-import { createModelPropertiesProviderFromConfig } from '../provider';
+import { createModelPropertiesProviderFromConfig } from '../property-provider';
 
 export interface Stats {
     structure: StructureWrapper,
@@ -54,7 +54,7 @@ export async function resolveJob(job: Job): Promise<CifWriter.Encoder<any>> {
         ConsoleLogger.logId(job.id, 'Query', 'Query finished.');
 
         perf.start('encode');
-        encoder.startDataBlock(structure.units[0].model.label.toUpperCase());
+        encoder.startDataBlock(structure.models[0].label.toUpperCase());
         encoder.writeCategory(_model_server_result, [job]);
         encoder.writeCategory(_model_server_params, [job]);
 
@@ -132,7 +132,7 @@ const _model_server_error_fields: CifField<number, string>[] = [
 const _model_server_stats_fields: CifField<number, Stats>[] = [
     int32<Stats>('io_time_ms', ctx => ctx.structure.info.readTime | 0),
     int32<Stats>('parse_time_ms', ctx => ctx.structure.info.parseTime | 0),
-    int32<Stats>('attach_props_time_ms', ctx => ctx.structure.info.attachPropsTime | 0),
+    // int32<Stats>('attach_props_time_ms', ctx => ctx.structure.info.attachPropsTime | 0),
     int32<Stats>('create_model_time_ms', ctx => ctx.structure.info.createModelTime | 0),
     int32<Stats>('query_time_ms', ctx => ctx.queryTimeMs | 0),
     int32<Stats>('encode_time_ms', ctx => ctx.encodeTimeMs | 0)

+ 1 - 1
src/servers/model/server/structure-wrapper.ts

@@ -14,7 +14,7 @@ import * as fs from 'fs'
 import * as zlib from 'zlib'
 import { Job } from './jobs';
 import { ConsoleLogger } from 'mol-util/console-logger';
-import { ModelPropertiesProvider } from '../provider';
+import { ModelPropertiesProvider } from '../property-provider';
 
 require('util.promisify').shim();