Browse Source

basic support for missing residues

Alexander Rose 5 years ago
parent
commit
f9d8942814

+ 33 - 9
src/mol-model-formats/structure/mmcif/parser.ts

@@ -12,7 +12,7 @@ import { Tensor, Vec3 } from '../../../mol-math/linear-algebra';
 import { RuntimeContext } from '../../../mol-task';
 import UUID from '../../../mol-util/uuid';
 import { Model } from '../../../mol-model/structure/model/model';
-import { Entities } from '../../../mol-model/structure/model/properties/common';
+import { Entities, ChemicalComponent, MissingResidue } from '../../../mol-model/structure/model/properties/common';
 import { CustomProperties } from '../../../mol-model/structure';
 import { ModelSymmetry } from '../../../mol-model/structure/model/properties/symmetry';
 import { createAssemblies } from './assembly';
@@ -23,7 +23,6 @@ import { getSecondaryStructure } from './secondary-structure';
 import { getSequence } from './sequence';
 import { sortAtomSite } from './sort';
 import { StructConn } from './bonds/struct_conn';
-import { ChemicalComponent } from '../../../mol-model/structure/model/properties/chemical-component';
 import { getMoleculeType, MoleculeType, getEntityType } from '../../../mol-model/structure/model/types';
 import { ModelFormat } from '../format';
 import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, SaccharideCompIdMap, UnknownSaccharideComponent } from '../../../mol-model/structure/structure/carbohydrates/constants';
@@ -98,14 +97,36 @@ function getModifiedResidueNameMap(format: mmCIF_Format): Model['properties']['m
     return { parentId, details };
 }
 
+function getMissingResidues(format: mmCIF_Format): Model['properties']['missingResidues'] {
+    const map = new Map<string, MissingResidue>();
+    const c = format.data.pdbx_unobs_or_zero_occ_residues
+
+    const getKey = (model_num: number, asym_id: string, seq_id: number) => {
+        return `${model_num}|${asym_id}|${seq_id}`
+    }
+
+    for (let i = 0, il = c._rowCount; i < il; ++i) {
+        const key = getKey(c.PDB_model_num.value(i), c.label_asym_id.value(i), c.label_seq_id.value(i))
+        map.set(key, { polymer_flag: c.polymer_flag.value(i), occupancy_flag: c.occupancy_flag.value(i) })
+    }
+
+    return {
+        has: (model_num: number, asym_id: string, seq_id: number) => {
+            return map.has(getKey(model_num, asym_id, seq_id))
+        },
+        get: (model_num: number, asym_id: string, seq_id: number) => {
+            return map.get(getKey(model_num, asym_id, seq_id))
+        },
+        size: map.size
+    }
+}
+
 function getChemicalComponentMap(format: mmCIF_Format): Model['properties']['chemicalComponentMap'] {
     const map = new Map<string, ChemicalComponent>();
     const { chem_comp } = format.data
-    if (chem_comp._rowCount > 0) {
-        const { id } = format.data.chem_comp
-        for (let i = 0, il = id.rowCount; i < il; ++i) {
-            map.set(id.value(i), Table.getRow(format.data.chem_comp, i))
-        }
+    const { id } = chem_comp
+    for (let i = 0, il = id.rowCount; i < il; ++i) {
+        map.set(id.value(i), Table.getRow(chem_comp, i))
     }
     return map
 }
@@ -158,6 +179,7 @@ const getUniqueComponentNames = memoize1((format: mmCIF_Format) => {
 
 export interface FormatData {
     modifiedResidues: Model['properties']['modifiedResidues']
+    missingResidues: Model['properties']['missingResidues']
     chemicalComponentMap: Model['properties']['chemicalComponentMap']
     saccharideComponentMap: Model['properties']['saccharideComponentMap']
 }
@@ -165,6 +187,7 @@ export interface FormatData {
 function getFormatData(format: mmCIF_Format): FormatData {
     return {
         modifiedResidues: getModifiedResidueNameMap(format),
+        missingResidues: getMissingResidues(format),
         chemicalComponentMap: getChemicalComponentMap(format),
         saccharideComponentMap: getSaccharideComponentMap(format)
     }
@@ -172,11 +195,12 @@ function getFormatData(format: mmCIF_Format): FormatData {
 
 function createStandardModel(format: mmCIF_Format, atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, formatData: FormatData, previous?: Model): Model {
     const atomic = getAtomicHierarchyAndConformation(atom_site, sourceIndex, entities, formatData, previous);
+    const modelNum = atom_site.pdbx_PDB_model_num.value(0)
     if (previous && atomic.sameAsPrevious) {
         return {
             ...previous,
             id: UUID.create22(),
-            modelNum: atom_site.pdbx_PDB_model_num.value(0),
+            modelNum,
             atomicConformation: atomic.conformation,
             _dynamicPropertyData: Object.create(null)
         };
@@ -192,7 +216,7 @@ function createStandardModel(format: mmCIF_Format, atom_site: AtomSite, sourceIn
         label,
         entry: label,
         sourceData: format,
-        modelNum: atom_site.pdbx_PDB_model_num.value(0),
+        modelNum,
         entities,
         symmetry: getSymmetry(format),
         sequence: getSequence(format.data, entities, atomic.hierarchy, formatData.modifiedResidues.parentId),

+ 3 - 2
src/mol-model/structure/model/model.ts

@@ -10,12 +10,11 @@ import StructureSequence from './properties/sequence';
 import { AtomicHierarchy, AtomicConformation } from './properties/atomic';
 import { ModelSymmetry } from './properties/symmetry';
 import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
-import { Entities } from './properties/common';
+import { Entities, ChemicalComponentMap, MissingResidues } from './properties/common';
 import { CustomProperties } from '../common/custom-property';
 import { SecondaryStructure } from './properties/seconday-structure';
 import { SaccharideComponentMap } from '../structure/carbohydrates/constants';
 import { ModelFormat } from '../../../mol-model-formats/structure/format';
-import { ChemicalComponentMap } from './properties/chemical-component';
 
 /**
  * Interface to the "source data" of the molecule.
@@ -49,6 +48,8 @@ export interface Model extends Readonly<{
             parentId: ReadonlyMap<string, string>,
             details: ReadonlyMap<string, string>
         }>,
+        /** map that holds details about unobserved or zero occurrence residues */
+        readonly missingResidues: MissingResidues,
         /** maps residue name to `ChemicalComponent` data */
         readonly chemicalComponentMap: ChemicalComponentMap
         /** maps residue name to `SaccharideComponent` data */

+ 0 - 13
src/mol-model/structure/model/properties/chemical-component.ts

@@ -1,13 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
-import { Table } from '../../../../mol-data/db';
-
-export type ChemicalComponent = Table.Row<mmCIF_Schema['chem_comp']>
-export type ChemicalComponentMap = ReadonlyMap<string, ChemicalComponent>
-
-// TODO add data for common chemical components

+ 18 - 3
src/mol-model/structure/model/properties/common.ts

@@ -1,13 +1,28 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { mmCIF_Database as mmCIF } from '../../../../mol-io/reader/cif/schema/mmcif'
+import { mmCIF_Database, mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif'
+import { Table } from '../../../../mol-data/db';
 import { EntityIndex } from '../indexing';
 
 export interface Entities {
-    data: mmCIF['entity'],
+    data: mmCIF_Database['entity'],
     getEntityIndex(id: string): EntityIndex
+}
+
+export type ChemicalComponent = Table.Row<mmCIF_Schema['chem_comp']>
+export type ChemicalComponentMap = ReadonlyMap<string, ChemicalComponent>
+
+export type MissingResidue = Table.Row<Pick<
+    mmCIF_Schema['pdbx_unobs_or_zero_occ_residues'],
+    'polymer_flag' | 'occupancy_flag'>
+>
+export interface MissingResidues {
+    has(model_num: number, asym_id: string, seq_id: number): boolean
+    get(model_num: number, asym_id: string, seq_id: number): MissingResidue | undefined
+    readonly size: number
 }

+ 1 - 1
src/mol-model/structure/model/properties/utils/atomic-derived.ts

@@ -5,11 +5,11 @@
  */
 
 import { AtomicData } from '../atomic';
-import { ChemicalComponentMap } from '../chemical-component';
 import { AtomicIndex, AtomicDerivedData } from '../atomic/hierarchy';
 import { ElementIndex, ResidueIndex } from '../../indexing';
 import { MoleculeType, getMoleculeType, getComponentType } from '../../types';
 import { getAtomIdForAtomRole } from '../../../../../mol-model/structure/util';
+import { ChemicalComponentMap } from '../common';
 
 export function getAtomicDerivedData(data: AtomicData, index: AtomicIndex, chemicalComponentMap: ChemicalComponentMap): AtomicDerivedData {
     const { label_comp_id, _rowCount: n } = data.residues

+ 1 - 1
src/mol-model/structure/model/properties/utils/coarse-ranges.ts

@@ -7,8 +7,8 @@
 import { CoarseRanges, CoarseElementData } from '../coarse/hierarchy';
 import { Segmentation, Interval } from '../../../../../mol-data/int';
 import SortedRanges from '../../../../../mol-data/int/sorted-ranges';
-import { ChemicalComponent } from '../chemical-component';
 import { ElementIndex } from '../../indexing';
+import { ChemicalComponent } from '../common';
 
 // TODO assumes all coarse elements are part of a polymer
 // TODO add gaps at the ends of the chains by comparing to the polymer sequence data

+ 13 - 2
src/mol-plugin/ui/sequence/polymer.ts

@@ -9,13 +9,18 @@ import { SequenceWrapper } from './util';
 import { OrderedSet, Interval } from '../../../mol-data/int';
 import { Loci } from '../../../mol-model/loci';
 import { Sequence } from '../../../mol-model/sequence';
-import { Color } from '../../../mol-util/color';
+import { MissingResidues } from '../../../mol-model/structure/model/properties/common';
+import { ColorNames } from '../../../mol-util/color/tables';
 
 export type StructureUnit = { structure: Structure, unit: Unit }
 
 export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
     private readonly location: StructureElement
     private readonly sequence: Sequence
+    private readonly missing: MissingResidues
+
+    private readonly modelNum: number
+    private readonly asymId: string
 
     seqId(i: number) {
         return this.sequence.offset + i + 1
@@ -24,7 +29,9 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
         return this.sequence.sequence[i]
     }
     residueColor(i: number) {
-        return Color(0)
+        return this.missing.has(this.modelNum, this.asymId, this.seqId(i))
+            ? ColorNames.grey
+            : ColorNames.black
     }
 
     eachResidue(loci: Loci, apply: (interval: Interval) => boolean) {
@@ -69,6 +76,10 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
 
         this.sequence = sequence
         this.location = StructureElement.create()
+        this.missing = data.unit.model.properties.missingResidues
+
+        this.modelNum = data.unit.model.modelNum
+        this.asymId = SP.chain.label_asym_id(l)
     }
 }