Skip to content
Snippets Groups Projects
Select Git revision
  • 3dc65bcad83e730c8f957f4a457ef4bc6f91963b
  • master default protected
  • rednatco-v2
  • rednatco
  • test
  • ntc-tube-uniform-color
  • ntc-tube-missing-atoms
  • restore-vertex-array-per-program
  • watlas2
  • dnatco_new
  • cleanup-old-nodejs
  • webmmb
  • fix_auth_seq_id
  • update_deps
  • ext_dev
  • ntc_balls
  • nci-2
  • plugin
  • bugfix-0.4.5
  • nci
  • servers
  • v0.5.0-dev.1
  • v0.4.5
  • v0.4.4
  • v0.4.3
  • v0.4.2
  • v0.4.1
  • v0.4.0
  • v0.3.12
  • v0.3.11
  • v0.3.10
  • v0.3.9
  • v0.3.8
  • v0.3.7
  • v0.3.6
  • v0.3.5
  • v0.3.4
  • v0.3.3
  • v0.3.2
  • v0.3.1
  • v0.3.0
41 results

scheduler.ts

Blame
  • model.ts 13.24 KiB
    /**
     * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
     *
     * @author David Sehnal <david.sehnal@gmail.com>
     * @author Alexander Rose <alexander.rose@weirdbyte.de>
     */
    
    import UUID from '../../../mol-util/uuid';
    import StructureSequence from './properties/sequence';
    import { AtomicHierarchy, AtomicConformation, AtomicRanges } from './properties/atomic';
    import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
    import { Entities, ChemicalComponentMap, MissingResidues, StructAsymMap } from './properties/common';
    import { CustomProperties } from '../../custom-property';
    import { SaccharideComponentMap } from '../structure/carbohydrates/constants';
    import { ModelFormat } from '../../../mol-model-formats/format';
    import { calcModelCenter, getAsymIdCount } from './util';
    import { Vec3 } from '../../../mol-math/linear-algebra';
    import { Mutable } from '../../../mol-util/type-helpers';
    import { Coordinates } from '../coordinates';
    import { Topology } from '../topology';
    import { Task } from '../../../mol-task';
    import { IndexPairBonds } from '../../../mol-model-formats/structure/property/bonds/index-pair';
    import { createModels } from '../../../mol-model-formats/structure/basic/parser';
    import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
    import { ChainIndex } from './indexing';
    import { SymmetryOperator } from '../../../mol-math/geometry';
    import { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry';
    import { Column } from '../../../mol-data/db';
    import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
    
    /**
     * Interface to the "source data" of the molecule.
     *
     * "Atoms" are integers in the range [0, atomCount).
     */
    export interface Model extends Readonly<{
        id: UUID,
        entryId: string,
        label: string,
    
        /** the name of the entry/file/collection the model is part of */
        entry: string,
    
        /**
         * corresponds to
         * - for IHM: `ihm_model_list.model_id`
         * - for standard mmCIF: `atom_site.pdbx_PDB_model_num`
         * - for models from coordinates: frame index
         */
        modelNum: number,
    
        sourceData: ModelFormat,
    
        entities: Entities,
        sequence: StructureSequence,
    
        atomicHierarchy: AtomicHierarchy,
        atomicConformation: AtomicConformation,
        atomicRanges: AtomicRanges,
        atomicChainOperatorMappinng: Map<ChainIndex, SymmetryOperator>,
    
        properties: {
            /** 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 */
            readonly saccharideComponentMap: SaccharideComponentMap
            /** maps label_asym_id name to `StructAsym` data */
            readonly structAsymMap: StructAsymMap
        },
    
        customProperties: CustomProperties,
    
        /**
         * Not to be accessed directly, each custom property descriptor
         * defines property accessors that use this field to store the data.
         */
        _staticPropertyData: { [name: string]: any },
        _dynamicPropertyData: { [name: string]: any },
    
        coarseHierarchy: CoarseHierarchy,
        coarseConformation: CoarseConformation
    }> {
    
    } { }
    
    export namespace Model {
        export type Trajectory = ReadonlyArray<Model>
    
        function _trajectoryFromModelAndCoordinates(model: Model, coordinates: Coordinates) {
            const trajectory: Mutable<Model.Trajectory> = [];
            const { frames } = coordinates;
    
            const srcIndex = model.atomicHierarchy.atoms.sourceIndex;
            const isIdentity = Column.isIdentity(srcIndex);
            const srcIndexArray = isIdentity ? void 0 : srcIndex.toArray({ array: Int32Array });
    
            for (let i = 0, il = frames.length; i < il; ++i) {
                const f = frames[i];
                const m = {
                    ...model,
                    id: UUID.create22(),
                    modelNum: i,
                    atomicConformation: isIdentity
                        ? Coordinates.getAtomicConformation(f, model.atomicConformation.atomId)
                        : Coordinates.getAtomicConformationReordered(f, model.atomicConformation.atomId, srcIndexArray!),
                    // TODO: add support for supplying sphere and gaussian coordinates in addition to atomic coordinates?
                    // coarseConformation: coarse.conformation,
                    customProperties: new CustomProperties(),
                    _staticPropertyData: Object.create(null),
                    _dynamicPropertyData: Object.create(null)
                };
                trajectory.push(m);
            }
            return { trajectory, srcIndexArray };
        }
    
        export function trajectoryFromModelAndCoordinates(model: Model, coordinates: Coordinates): Trajectory {
            return _trajectoryFromModelAndCoordinates(model, coordinates).trajectory;
        }
    
        function invertIndex(xs: ArrayLike<number>) {
            const ret = new Int32Array(xs.length);
            for (let i = 0, _i = xs.length; i < _i; i++) {
                ret[xs[i]] = i;
            }
            return ret;
        }
    
        export function trajectoryFromTopologyAndCoordinates(topology: Topology, coordinates: Coordinates): Task<Trajectory> {
            return Task.create('Create Trajectory', async ctx => {
                const model = (await createModels(topology.basic, topology.sourceData, ctx))[0];
                if (!model) throw new Error('found no model');
                const { trajectory, srcIndexArray } = _trajectoryFromModelAndCoordinates(model, coordinates);
    
                // TODO: cache the inverted index somewhere?
                const invertedIndex = srcIndexArray ? invertIndex(srcIndexArray) : void 0;
                const pairs = srcIndexArray
                    ? {
                        indexA: Column.ofIntArray(Column.mapToArray(topology.bonds.indexA, i => invertedIndex![i], Int32Array)),
                        indexB: Column.ofIntArray(Column.mapToArray(topology.bonds.indexB, i => invertedIndex![i], Int32Array)),
                        order: topology.bonds.order
                    }
                    : topology.bonds;
    
                const bondData = { pairs, count: model.atomicHierarchy.atoms._rowCount };
                const indexPairBonds = IndexPairBonds.fromData(bondData);
    
                let index = 0;
                for (const m of trajectory) {
                    IndexPairBonds.Provider.set(m, indexPairBonds);
                    TrajectoryInfo.set(m, { index: index++, size: trajectory.length });
                }
                return trajectory;
            });
        }
    
        const CenterProp = '__Center__';
        export function getCenter(model: Model): Vec3 {
            if (model._dynamicPropertyData[CenterProp]) return model._dynamicPropertyData[CenterProp];
            const center = calcModelCenter(model.atomicConformation, model.coarseConformation);
            model._dynamicPropertyData[CenterProp] = center;
            return center;
        }
    
        const TrajectoryInfoProp = '__TrajectoryInfo__';
        export type TrajectoryInfo = { readonly index: number, readonly size: number }
        export const TrajectoryInfo = {
            get(model: Model): TrajectoryInfo {
                return model._staticPropertyData[TrajectoryInfoProp] || { index: 0, size: 1 };
            },
            set(model: Model, trajectoryInfo: TrajectoryInfo) {
                return model._staticPropertyData[TrajectoryInfoProp] = trajectoryInfo;
            }
        };
    
        const AsymIdCountProp = '__AsymIdCount__';
        export type AsymIdCount = { readonly auth: number, readonly label: number }
        export const AsymIdCount = {
            get(model: Model): AsymIdCount {
                if (model._staticPropertyData[AsymIdCountProp]) return model._staticPropertyData[AsymIdCountProp];
                const asymIdCount = getAsymIdCount(model);
                model._staticPropertyData[AsymIdCountProp] = asymIdCount;
                return asymIdCount;
            },
        };
    
        export type AsymIdOffset = { auth: number, label: number };
        export const AsymIdOffset = CustomModelProperty.createSimple<AsymIdOffset>('asym_id_offset', 'static');
    
        export type Index = number;
        export const Index = CustomModelProperty.createSimple<Index>('index', 'static');
    
        //
    
        export function hasCarbohydrate(model: Model): boolean {
            return model.properties.saccharideComponentMap.size > 0;
        }
    
        export function hasProtein(model: Model): boolean {
            const { subtype } = model.entities;
            for (let i = 0, il = subtype.rowCount; i < il; ++i) {
                if (subtype.value(i).startsWith('polypeptide')) return true;
            }
            return false;
        }
    
        export function hasNucleic(model: Model): boolean {
            const { subtype } = model.entities;
            for (let i = 0, il = subtype.rowCount; i < il; ++i) {
                const s = subtype.value(i);
                if (s.endsWith('ribonucleotide hybrid') || s.endsWith('ribonucleotide')) return true;
            }
            return false;
        }
    
        export function isFromPdbArchive(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            const { db } = model.sourceData.data;
            return (
                db.database_2.database_id.isDefined ||
                // 4 character PDB id
                model.entryId.match(/^[1-9][a-z0-9]{3,3}$/i) !== null ||
                // long PDB id
                model.entryId.match(/^pdb_[0-9]{4,4}[1-9][a-z0-9]{3,3}$/i) !== null
            );
        }
    
        export function hasSecondaryStructure(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            const { db } = model.sourceData.data;
            return (
                db.struct_conf.id.isDefined ||
                db.struct_sheet_range.id.isDefined
            );
        }
    
        const tmpAngles90 = Vec3.create(1.5707963, 1.5707963, 1.5707963); // in radians
        const tmpLengths1 = Vec3.create(1, 1, 1);
        export function hasCrystalSymmetry(model: Model): boolean {
            const spacegroup = ModelSymmetry.Provider.get(model)?.spacegroup;
            return !!spacegroup && !(
                spacegroup.num === 1 &&
                Vec3.equals(spacegroup.cell.anglesInRadians, tmpAngles90) &&
                Vec3.equals(spacegroup.cell.size, tmpLengths1)
            );
        }
    
        export function isFromXray(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            const { db } = model.sourceData.data;
            for (let i = 0; i < db.exptl.method.rowCount; i++) {
                const v = db.exptl.method.value(i).toUpperCase();
                if (v.indexOf('DIFFRACTION') >= 0) return true;
            }
            return false;
        }
    
        export function isFromEm(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            const { db } = model.sourceData.data;
            for (let i = 0; i < db.exptl.method.rowCount; i++) {
                const v = db.exptl.method.value(i).toUpperCase();
                if (v.indexOf('MICROSCOPY') >= 0) return true;
            }
            return false;
        }
    
        export function isFromNmr(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            const { db } = model.sourceData.data;
            for (let i = 0; i < db.exptl.method.rowCount; i++) {
                const v = db.exptl.method.value(i).toUpperCase();
                if (v.indexOf('NMR') >= 0) return true;
            }
            return false;
        }
    
        export function hasXrayMap(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            // Check exprimental method to exclude models solved with
            // 'ELECTRON CRYSTALLOGRAPHY' which also have structure factors
            if (!isFromXray(model)) return false;
            const { db } = model.sourceData.data;
            const { status_code_sf } = db.pdbx_database_status;
            return status_code_sf.isDefined && status_code_sf.value(0) === 'REL';
        }
    
        /**
         * Also checks for `content_type` of 'associated EM volume' to exclude cases
         * like 6TEK which are solved with 'X-RAY DIFFRACTION' but have an related
         * EMDB entry of type 'other EM volume'.
         */
        export function hasEmMap(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            const { db } = model.sourceData.data;
            const { db_name, content_type } = db.pdbx_database_related;
            for (let i = 0, il = db.pdbx_database_related._rowCount; i < il; ++i) {
                if (db_name.value(i).toUpperCase() === 'EMDB' && content_type.value(i) === 'associated EM volume') {
                    return true;
                }
            }
            return false;
        }
    
        export function hasDensityMap(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            return hasXrayMap(model) || hasEmMap(model);
        }
    
        export function probablyHasDensityMap(model: Model): boolean {
            if (!MmcifFormat.is(model.sourceData)) return false;
            const { db } = model.sourceData.data;
            return hasDensityMap(model) || (
                // check if from pdb archive but missing relevant meta data
                isFromPdbArchive(model) && (
                    !db.exptl.method.isDefined ||
                    (isFromXray(model) && (
                        !db.pdbx_database_status.status_code_sf.isDefined ||
                        db.pdbx_database_status.status_code_sf.valueKind(0) === Column.ValueKind.Unknown
                    )) ||
                    (isFromEm(model) && (
                        !db.pdbx_database_related.db_name.isDefined
                    ))
                )
            );
        }
    }