Ver Fonte

Generate structAsymMap from normalized atomic hierarchy
- Fixes issue with remapped chains
- Requires to parse PRD separately

dsehnal há 3 anos atrás
pai
commit
4871f1547c

+ 9 - 9
src/mol-model-formats/structure/basic/atomic.ts

@@ -7,21 +7,21 @@
 
 import { Column, Table } from '../../../mol-data/db';
 import { Interval, Segmentation } from '../../../mol-data/int';
-import { UUID } from '../../../mol-util/uuid';
-import { ElementIndex, ChainIndex } from '../../../mol-model/structure';
+import { toDatabase } from '../../../mol-io/reader/cif/schema';
+import { SymmetryOperator } from '../../../mol-math/geometry';
+import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
+import { ChainIndex, ElementIndex } from '../../../mol-model/structure';
+import { AtomSiteOperatorMappingSchema } from '../../../mol-model/structure/export/categories/atom_site_operator_mapping';
 import { Model } from '../../../mol-model/structure/model/model';
 import { AtomicConformation, AtomicData, AtomicHierarchy, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from '../../../mol-model/structure/model/properties/atomic';
-import { getAtomicIndex } from '../../../mol-model/structure/model/properties/utils/atomic-index';
-import { ElementSymbol } from '../../../mol-model/structure/model/types';
 import { Entities } from '../../../mol-model/structure/model/properties/common';
 import { getAtomicDerivedData } from '../../../mol-model/structure/model/properties/utils/atomic-derived';
-import { AtomSite } from './schema';
+import { getAtomicIndex } from '../../../mol-model/structure/model/properties/utils/atomic-index';
+import { ElementSymbol } from '../../../mol-model/structure/model/types';
+import { UUID } from '../../../mol-util/uuid';
 import { ModelFormat } from '../../format';
-import { SymmetryOperator } from '../../../mol-math/geometry';
 import { MmcifFormat } from '../mmcif';
-import { AtomSiteOperatorMappingSchema } from '../../../mol-model/structure/export/categories/atom_site_operator_mapping';
-import { toDatabase } from '../../../mol-io/reader/cif/schema';
-import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
+import { AtomSite } from './schema';
 
 function findHierarchyOffsets(atom_site: AtomSite) {
     if (atom_site._rowCount === 0) return { residues: [], chains: [] };

+ 3 - 3
src/mol-model-formats/structure/basic/coarse.ts

@@ -30,7 +30,7 @@ export interface CoarseData {
 
 export const EmptyCoarse = { hierarchy: CoarseHierarchy.Empty, conformation: void 0 as any };
 
-export function getCoarse(data: CoarseData, properties: Model['properties']): { hierarchy: CoarseHierarchy, conformation: CoarseConformation } {
+export function getCoarse(data: CoarseData, chemicalComponentMap: Model['properties']['chemicalComponentMap']): { hierarchy: CoarseHierarchy, conformation: CoarseConformation } {
     const { ihm_sphere_obj_site, ihm_gaussian_obj_site } = data;
 
     if (ihm_sphere_obj_site._rowCount === 0 && ihm_gaussian_obj_site._rowCount === 0) return EmptyCoarse;
@@ -38,12 +38,12 @@ export function getCoarse(data: CoarseData, properties: Model['properties']): {
     const sphereData = getData(ihm_sphere_obj_site);
     const sphereConformation = getSphereConformation(ihm_sphere_obj_site);
     const sphereKeys = getCoarseKeys(sphereData, data.entities);
-    const sphereRanges = getCoarseRanges(sphereData, properties.chemicalComponentMap);
+    const sphereRanges = getCoarseRanges(sphereData, chemicalComponentMap);
 
     const gaussianData = getData(ihm_gaussian_obj_site);
     const gaussianConformation = getGaussianConformation(ihm_gaussian_obj_site);
     const gaussianKeys = getCoarseKeys(gaussianData, data.entities);
-    const gaussianRanges = getCoarseRanges(gaussianData, properties.chemicalComponentMap);
+    const gaussianRanges = getCoarseRanges(gaussianData, chemicalComponentMap);
 
     return {
         hierarchy: {

+ 21 - 17
src/mol-model-formats/structure/basic/entities.ts

@@ -11,7 +11,7 @@ import { getEntityType, getEntitySubtype } from '../../../mol-model/structure/mo
 import { ElementIndex, EntityIndex, Model } from '../../../mol-model/structure/model';
 import { BasicData, BasicSchema, Entity } from './schema';
 
-export function getEntities(data: BasicData, properties: Model['properties']): Entities {
+export function getEntityData(data: BasicData): Entities {
     let entityData: Entity;
 
     if (!data.entity.id.isDefined) {
@@ -121,28 +121,32 @@ export function getEntities(data: BasicData, properties: Model['properties']): E
 
     const subtypeColumn = Column.ofArray({ array: subtypes, schema: EntitySubtype });
 
-    //
+    return {
+        data: entityData,
+        subtype: subtypeColumn,
+        getEntityIndex
+    };
+}
 
-    const prdIds: string[] = new Array(entityData._rowCount);
-    prdIds.fill('');
+export function getEntitiesWithPRD(data: BasicData, entities: Entities, structAsymMap: Model['properties']['structAsymMap']): Entities {
+    if (!data.pdbx_molecule || !data.pdbx_molecule.prd_id.isDefined) {
+        return entities;
+    }
 
-    if (data.pdbx_molecule && data.pdbx_molecule.prd_id.isDefined) {
-        const { asym_id, prd_id, _rowCount } = data.pdbx_molecule;
-        for (let i = 0; i < _rowCount; ++i) {
-            const asymId = asym_id.value(i);
-            const entityId = properties.structAsymMap.get(asymId)?.entity_id;
-            if (entityId !== undefined) {
-                prdIds[getEntityIndex(entityId)] = prd_id.value(i);
-            }
+    const prdIds: string[] = new Array(entities.data._rowCount);
+    prdIds.fill('');
+    const { asym_id, prd_id, _rowCount } = data.pdbx_molecule;
+    for (let i = 0; i < _rowCount; ++i) {
+        const asymId = asym_id.value(i);
+        const entityId = structAsymMap.get(asymId)?.entity_id;
+        if (entityId !== undefined) {
+            prdIds[entities.getEntityIndex(entityId)] = prd_id.value(i);
         }
     }
-
     const prdIdColumn = Column.ofArray({ array: prdIds, schema: Column.Schema.str });
 
     return {
-        data: entityData,
-        subtype: subtypeColumn,
-        prd_id: prdIdColumn,
-        getEntityIndex
+        ...entities,
+        prd_id: prdIdColumn
     };
 }

+ 35 - 16
src/mol-model-formats/structure/basic/parser.ts

@@ -18,13 +18,13 @@ import { sortAtomSite } from './sort';
 import { ModelFormat } from '../../format';
 import { getAtomicRanges } from '../../../mol-model/structure/model/properties/utils/atomic-ranges';
 import { AtomSite, BasicData } from './schema';
-import { getProperties } from './properties';
-import { getEntities } from './entities';
+import { getChemicalComponentMap, getMissingResidues, getSaccharideComponentMap, getStructAsymMap } from './properties';
+import { getEntitiesWithPRD, getEntityData } from './entities';
 import { getModelGroupName } from './util';
 import { ArrayTrajectory } from '../../../mol-model/structure/trajectory';
 
 export async function createModels(data: BasicData, format: ModelFormat, ctx: RuntimeContext) {
-    const properties = getProperties(data, format);
+    const properties = getCommonProperties(data, format);
     const models = data.ihm_model_list._rowCount > 0
         ? await readIntegrative(ctx, data, properties, format)
         : await readStandard(ctx, data, properties, format);
@@ -36,9 +36,18 @@ export async function createModels(data: BasicData, format: ModelFormat, ctx: Ru
     return new ArrayTrajectory(models);
 }
 
-/** Standard atomic model */
-function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, properties: Model['properties'], format: ModelFormat, previous?: Model): Model {
+type CommonProperties = Omit<Model['properties'], 'structAsymMap'>
 
+function getCommonProperties(data: BasicData, format: ModelFormat): CommonProperties {
+    return {
+        missingResidues: getMissingResidues(data),
+        chemicalComponentMap: getChemicalComponentMap(data),
+        saccharideComponentMap: getSaccharideComponentMap(data)
+    };
+}
+
+/** Standard atomic model */
+function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, properties: CommonProperties, format: ModelFormat, previous?: Model): Model {
     const atomic = getAtomicHierarchyAndConformation(atom_site, sourceIndex, entities, properties.chemicalComponentMap, format, previous);
     const modelNum = atom_site.pdbx_PDB_model_num.value(0);
     if (previous && atomic.sameAsPrevious) {
@@ -54,6 +63,7 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
     const coarse = EmptyCoarse;
     const sequence = getSequence(data, entities, atomic.hierarchy, coarse.hierarchy);
     const atomicRanges = getAtomicRanges(atomic.hierarchy, entities, atomic.conformation, sequence);
+    const structAsymMap = getStructAsymMap(atomic.hierarchy);
 
     const entry = data.entry.id.valueKind(0) === Column.ValueKind.Present
         ? data.entry.id.value(0) : format.name;
@@ -70,7 +80,7 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
         sourceData: format,
         modelNum,
         parent: undefined,
-        entities,
+        entities: getEntitiesWithPRD(data, entities, structAsymMap),
         sequence,
         atomicHierarchy: atomic.hierarchy,
         atomicConformation: atomic.conformation,
@@ -78,7 +88,10 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
         atomicChainOperatorMappinng: atomic.chainOperatorMapping,
         coarseHierarchy: coarse.hierarchy,
         coarseConformation: coarse.conformation,
-        properties,
+        properties: {
+            ...properties,
+            structAsymMap
+        },
         customProperties: new CustomProperties(),
         _staticPropertyData: Object.create(null),
         _dynamicPropertyData: Object.create(null)
@@ -86,9 +99,9 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
 }
 
 /** Integrative model with atomic/coarse parts */
-function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Model['properties'], format: ModelFormat): Model {
+function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: CommonProperties, format: ModelFormat): Model {
     const atomic = getAtomicHierarchyAndConformation(ihm.atom_site, ihm.atom_site_sourceIndex, ihm.entities, properties.chemicalComponentMap, format);
-    const coarse = getCoarse(ihm, properties);
+    const coarse = getCoarse(ihm, properties.chemicalComponentMap);
     const sequence = getSequence(data, ihm.entities, atomic.hierarchy, coarse.hierarchy);
     const atomicRanges = getAtomicRanges(atomic.hierarchy, ihm.entities, atomic.conformation, sequence);
 
@@ -101,6 +114,9 @@ function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Mo
     if (ihm.model_name) label.push(ihm.model_name);
     if (ihm.model_group_name) label.push(ihm.model_group_name);
 
+    // TODO: should this contain anything from coarse hierarchy?
+    const structAsymMap = getStructAsymMap(atomic.hierarchy);
+
     return {
         id: UUID.create22(),
         entryId: entry,
@@ -109,7 +125,7 @@ function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Mo
         sourceData: format,
         modelNum: ihm.model_id,
         parent: undefined,
-        entities: ihm.entities,
+        entities: getEntitiesWithPRD(data, ihm.entities, structAsymMap),
         sequence,
         atomicHierarchy: atomic.hierarchy,
         atomicConformation: atomic.conformation,
@@ -117,7 +133,10 @@ function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Mo
         atomicChainOperatorMappinng: atomic.chainOperatorMapping,
         coarseHierarchy: coarse.hierarchy,
         coarseConformation: coarse.conformation,
-        properties,
+        properties: {
+            ...properties,
+            structAsymMap
+        },
         customProperties: new CustomProperties(),
         _staticPropertyData: Object.create(null),
         _dynamicPropertyData: Object.create(null)
@@ -132,12 +151,12 @@ function findModelEnd(num: Column<number>, startIndex: number) {
     return endIndex;
 }
 
-async function readStandard(ctx: RuntimeContext, data: BasicData, properties: Model['properties'], format: ModelFormat) {
+async function readStandard(ctx: RuntimeContext, data: BasicData, properties: CommonProperties, format: ModelFormat) {
     const models: Model[] = [];
 
     if (data.atom_site) {
         const atomCount = data.atom_site.id.rowCount;
-        const entities = getEntities(data, properties);
+        const entities = getEntityData(data);
 
         let modelStart = 0;
         while (modelStart < atomCount) {
@@ -170,8 +189,8 @@ function splitTable<T extends Table<any>>(table: T, col: Column<number>) {
 
 
 
-async function readIntegrative(ctx: RuntimeContext, data: BasicData, properties: Model['properties'], format: ModelFormat) {
-    const entities = getEntities(data, properties);
+async function readIntegrative(ctx: RuntimeContext, data: BasicData, properties: CommonProperties, format: ModelFormat) {
+    const entities = getEntityData(data);
     // when `atom_site.ihm_model_id` is undefined fall back to `atom_site.pdbx_PDB_model_num`
     const atom_sites_modelColumn = data.atom_site.ihm_model_id.isDefined
         ? data.atom_site.ihm_model_id : data.atom_site.pdbx_PDB_model_num;
@@ -206,7 +225,7 @@ async function readIntegrative(ctx: RuntimeContext, data: BasicData, properties:
                 model_id: id,
                 model_name: model_name.value(i),
                 model_group_name: getModelGroupName(id, data),
-                entities: entities,
+                entities,
                 atom_site,
                 atom_site_sourceIndex,
                 ihm_sphere_obj_site: sphere_sites.has(id) ? sphere_sites.get(id)!.table : Table.window(data.ihm_sphere_obj_site, data.ihm_sphere_obj_site._schema, 0, 0),

+ 12 - 48
src/mol-model-formats/structure/basic/properties.ts

@@ -5,18 +5,16 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
+import { Table } from '../../../mol-data/db';
 import { Model } from '../../../mol-model/structure/model/model';
+import { AtomicHierarchy } from '../../../mol-model/structure/model/properties/atomic';
 import { ChemicalComponent, MissingResidue, StructAsym } from '../../../mol-model/structure/model/properties/common';
-import { getMoleculeType, MoleculeType, getDefaultChemicalComponent } from '../../../mol-model/structure/model/types';
-import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, SaccharideCompIdMap, UnknownSaccharideComponent } from '../../../mol-model/structure/structure/carbohydrates/constants';
+import { getDefaultChemicalComponent, getMoleculeType, MoleculeType } from '../../../mol-model/structure/model/types';
+import { SaccharideCompIdMap, SaccharideComponent, SaccharideComponentMap, SaccharidesSnfgMap, UnknownSaccharideComponent } from '../../../mol-model/structure/structure/carbohydrates/constants';
 import { memoize1 } from '../../../mol-util/memoize';
 import { BasicData } from './schema';
-import { Table } from '../../../mol-data/db';
-import { ModelFormat } from '../../format';
-import { MmcifFormat } from '../mmcif';
-import { AtomSiteOperatorMappingCategoryName } from '../../../mol-model/structure/export/categories/atom_site_operator_mapping';
 
-function getMissingResidues(data: BasicData): Model['properties']['missingResidues'] {
+export function getMissingResidues(data: BasicData): Model['properties']['missingResidues'] {
     const map = new Map<string, MissingResidue>();
     const getKey = (model_num: number, asym_id: string, seq_id: number) => {
         return `${model_num}|${asym_id}|${seq_id}`;
@@ -39,7 +37,7 @@ function getMissingResidues(data: BasicData): Model['properties']['missingResidu
     };
 }
 
-function getChemicalComponentMap(data: BasicData): Model['properties']['chemicalComponentMap'] {
+export function getChemicalComponentMap(data: BasicData): Model['properties']['chemicalComponentMap'] {
     const map = new Map<string, ChemicalComponent>();
 
     if (data.chem_comp._rowCount > 0) {
@@ -56,7 +54,7 @@ function getChemicalComponentMap(data: BasicData): Model['properties']['chemical
     return map;
 }
 
-function getSaccharideComponentMap(data: BasicData): SaccharideComponentMap {
+export function getSaccharideComponentMap(data: BasicData): SaccharideComponentMap {
     const map = new Map<string, SaccharideComponent>();
 
     if (data.pdbx_chem_comp_identifier._rowCount > 0) {
@@ -111,49 +109,15 @@ const getUniqueComponentNames = memoize1((data: BasicData) => {
 });
 
 
-function getStructAsymMap(data: BasicData, format: ModelFormat): Model['properties']['structAsymMap'] {
+export function getStructAsymMap(atomic: AtomicHierarchy): Model['properties']['structAsymMap'] {
     const map = new Map<string, StructAsym>();
 
-    // do not determine values from atom sites if mol_star atom site operator mapping is preset
-    const skipAtomSite = MmcifFormat.is(format)
-        && format.data.frame.categoryNames.indexOf(AtomSiteOperatorMappingCategoryName) >= 0
-        && data.struct_asym._rowCount > 0;
+    const { auth_asym_id, label_asym_id, label_entity_id } = atomic.chains;
 
-    if (!skipAtomSite) {
-        const { label_asym_id, auth_asym_id, label_entity_id } = data.atom_site;
-        for (let i = 0, il = label_asym_id.rowCount; i < il; ++i) {
-            const id = label_asym_id.value(i);
-            if (!map.has(id)) {
-                map.set(id, {
-                    id,
-                    auth_id: auth_asym_id.value(i),
-                    entity_id: label_entity_id.value(i)
-                });
-            }
-        }
+    for (let i = 0, _i = atomic.chains._rowCount; i < _i; i ++) {
+        const id = label_asym_id.value(i);
+        map.set(id, { id, auth_id: auth_asym_id.value(i), entity_id: label_entity_id.value(i) });
     }
 
-    if (data.struct_asym._rowCount > 0) {
-        const { id, entity_id } = data.struct_asym;
-        for (let i = 0, il = id.rowCount; i < il; ++i) {
-            const _id = id.value(i);
-            if (!map.has(_id)) {
-                map.set(_id, {
-                    id: _id,
-                    auth_id: '',
-                    entity_id: entity_id.value(i)
-                });
-            }
-        }
-    }
     return map;
-}
-
-export function getProperties(data: BasicData, format: ModelFormat): Model['properties'] {
-    return {
-        missingResidues: getMissingResidues(data),
-        chemicalComponentMap: getChemicalComponentMap(data),
-        saccharideComponentMap: getSaccharideComponentMap(data),
-        structAsymMap: getStructAsymMap(data, format)
-    };
 }

+ 1 - 1
src/mol-model/structure/model/properties/common.ts

@@ -22,7 +22,7 @@ export const EntitySubtype = Column.Schema.Aliased<EntitySubtype>(Column.Schema.
 export interface Entities {
     data: mmCIF_Database['entity'],
     subtype: Column<EntitySubtype>,
-    prd_id: Column<string>,
+    prd_id?: Column<string>,
     getEntityIndex(id: string): EntityIndex
 }
 

+ 1 - 1
src/mol-model/structure/structure/properties.ts

@@ -162,7 +162,7 @@ const entity = {
     pdbx_ec: p(l => l.unit.model.entities.data.pdbx_ec.value(eK(l))),
 
     subtype: p(l => l.unit.model.entities.subtype.value(eK(l))),
-    prd_id: p(l => l.unit.model.entities.prd_id.value(eK(l))),
+    prd_id: p(l => l.unit.model.entities.prd_id?.value(eK(l)) ?? ''),
 };
 
 const _emptyList: any[] = [];

+ 1 - 1
src/mol-theme/color/chain-id.ts

@@ -62,7 +62,7 @@ function getAsymIdSerialMap(structure: Structure, type: AsymIdType) {
         const offset = (type === 'auth' ? asymIdOffset?.auth : asymIdOffset?.label) || 0;
         let count = 0;
         m.properties.structAsymMap.forEach(({ auth_id }, label_id) => {
-            const asymId = type === 'auth' && auth_id ? auth_id : label_id;
+            const asymId = type === 'auth' ? auth_id : label_id;
             const k = structure.models.length > 1
                 ? getKey(m, asymId)
                 : asymId;