molstar
Version:
A comprehensive macromolecular library.
411 lines (410 loc) • 17.9 kB
JavaScript
/**
* Copyright (c) 2017-2024 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 { CustomProperties } from '../../custom-property';
import { calcModelCenter, getAsymIdCount } from './util';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { Coordinates } from '../coordinates';
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 { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry';
import { Column } from '../../../mol-data/db';
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
import { ArrayTrajectory } from '../trajectory';
import { ModelSecondaryStructure } from '../../../mol-model-formats/structure/property/secondary-structure';
{ }
export var Model;
(function (Model) {
function _trajectoryFromModelAndCoordinates(model, coordinates) {
const trajectory = [];
const { frames } = coordinates;
const srcIndexArray = getSourceIndexArray(model);
const coarseGrained = isCoarseGrained(model);
const elementCount = model.atomicHierarchy.atoms._rowCount;
for (let i = 0, il = frames.length; i < il; ++i) {
const f = frames[i];
if (f.elementCount !== elementCount) {
throw new Error(`Frame element count mismatch, got ${f.elementCount} but expected ${elementCount}.`);
}
const m = {
...model,
id: UUID.create22(),
modelNum: i,
atomicConformation: getAtomicConformationFromFrame(model, f),
// 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)
};
if (f.cell) {
const symmetry = ModelSymmetry.fromCell(f.cell.size, f.cell.anglesInRadians);
ModelSymmetry.Provider.set(m, symmetry);
}
Model.TrajectoryInfo.set(m, { index: i, size: frames.length });
Model.CoarseGrained.set(m, coarseGrained);
trajectory.push(m);
}
return { trajectory, srcIndexArray };
}
function getSourceIndexArray(model) {
const srcIndex = model.atomicHierarchy.atomSourceIndex;
let srcIndexArray = undefined;
if ('__srcIndexArray__' in model._staticPropertyData) {
srcIndexArray = model._dynamicPropertyData.__srcIndexArray__;
}
else {
srcIndexArray = Column.isIdentity(srcIndex) ? void 0 : srcIndex.toArray({ array: Int32Array });
model._dynamicPropertyData.__srcIndexArray__ = srcIndexArray;
}
return srcIndexArray;
}
function trajectoryFromModelAndCoordinates(model, coordinates) {
return new ArrayTrajectory(_trajectoryFromModelAndCoordinates(model, coordinates).trajectory);
}
Model.trajectoryFromModelAndCoordinates = trajectoryFromModelAndCoordinates;
function trajectoryFromTopologyAndCoordinates(topology, coordinates) {
return Task.create('Create Trajectory', async (ctx) => {
const models = await createModels(topology.basic, topology.sourceData, ctx);
if (models.frameCount === 0)
throw new Error('found no model');
const model = models.representative;
const { trajectory } = _trajectoryFromModelAndCoordinates(model, coordinates);
const bondData = { pairs: topology.bonds, count: model.atomicHierarchy.atoms._rowCount };
const indexPairBonds = IndexPairBonds.fromData(bondData);
const coarseGrained = isCoarseGrained(model);
let index = 0;
for (const m of trajectory) {
IndexPairBonds.Provider.set(m, indexPairBonds);
Model.TrajectoryInfo.set(m, { index: index++, size: trajectory.length });
Model.CoarseGrained.set(m, coarseGrained);
}
return new ArrayTrajectory(trajectory);
});
}
Model.trajectoryFromTopologyAndCoordinates = trajectoryFromTopologyAndCoordinates;
function getAtomicConformationFromFrame(model, frame) {
return Coordinates.getAtomicConformation(frame, {
atomId: model.atomicConformation.atomId,
occupancy: model.atomicConformation.occupancy,
B_iso_or_equiv: model.atomicConformation.B_iso_or_equiv
}, getSourceIndexArray(model));
}
Model.getAtomicConformationFromFrame = getAtomicConformationFromFrame;
const CenterProp = '__Center__';
function getCenter(model) {
if (model._dynamicPropertyData[CenterProp])
return model._dynamicPropertyData[CenterProp];
const center = calcModelCenter(model.atomicConformation, model.coarseConformation);
model._dynamicPropertyData[CenterProp] = center;
return center;
}
Model.getCenter = getCenter;
function invertIndex(xs) {
const invertedIndex = new Int32Array(xs.rowCount);
let isIdentity = false;
for (let i = 0, _i = xs.rowCount; i < _i; i++) {
const x = xs.value(i);
if (x !== i)
isIdentity = false;
invertedIndex[x] = i;
}
return { isIdentity, invertedIndex: invertedIndex };
}
const InvertedAtomSrcIndexProp = '__InvertedAtomSrcIndex__';
function getInvertedAtomSourceIndex(model) {
if (model._staticPropertyData[InvertedAtomSrcIndexProp])
return model._staticPropertyData[InvertedAtomSrcIndexProp];
const index = invertIndex(model.atomicHierarchy.atomSourceIndex);
model._staticPropertyData[InvertedAtomSrcIndexProp] = index;
return index;
}
Model.getInvertedAtomSourceIndex = getInvertedAtomSourceIndex;
const TrajectoryInfoProp = '__TrajectoryInfo__';
Model.TrajectoryInfo = {
get(model) {
return model._dynamicPropertyData[TrajectoryInfoProp] || { index: 0, size: 1 };
},
set(model, trajectoryInfo) {
return model._dynamicPropertyData[TrajectoryInfoProp] = trajectoryInfo;
}
};
const AsymIdCountProp = '__AsymIdCount__';
Model.AsymIdCount = {
get(model) {
if (model._dynamicPropertyData[AsymIdCountProp])
return model._dynamicPropertyData[AsymIdCountProp];
const asymIdCount = getAsymIdCount(model);
model._dynamicPropertyData[AsymIdCountProp] = asymIdCount;
return asymIdCount;
},
};
Model.AsymIdOffset = CustomModelProperty.createSimple('asym_id_offset', 'static');
Model.Index = CustomModelProperty.createSimple('index', 'static');
Model.MaxIndex = CustomModelProperty.createSimple('max_index', 'static');
function getRoot(model) {
return model.parent || model;
}
Model.getRoot = getRoot;
function areHierarchiesEqual(a, b) {
return a.atomicHierarchy === b.atomicHierarchy && a.coarseHierarchy === b.coarseHierarchy;
}
Model.areHierarchiesEqual = areHierarchiesEqual;
const CoordinatesHistoryProp = '__CoordinatesHistory__';
Model.CoordinatesHistory = {
get(model) {
return model._staticPropertyData[CoordinatesHistoryProp];
},
set(model, coordinatesHistory) {
return model._staticPropertyData[CoordinatesHistoryProp] = coordinatesHistory;
}
};
const CoarseGrainedProp = '__CoarseGrained__';
Model.CoarseGrained = {
get(model) {
return model._staticPropertyData[CoarseGrainedProp];
},
set(model, coarseGrained) {
return model._staticPropertyData[CoarseGrainedProp] = coarseGrained;
}
};
/**
* Mark as coarse grained if any of the following conditions are met:
* - has typical coarse grained atom names (BB, SC1)
* - has less than three times as many atoms as polymer residues (C-alpha only models)
* - has no standard sidechain atoms
*/
function isCoarseGrained(model) {
let coarseGrained = Model.CoarseGrained.get(model);
if (coarseGrained === undefined) {
let polymerResidueCount = 0;
let polymerDirectionCount = 0;
const { polymerType, directionToElementIndex } = model.atomicHierarchy.derived.residue;
for (let i = 0; i < polymerType.length; ++i) {
if (polymerType[i] !== 0 /* PolymerType.NA */) {
polymerResidueCount += 1;
if (directionToElementIndex[i] !== -1)
polymerDirectionCount += 1;
}
}
// check for coarse grained atom names
let hasBB = false, hasSC1 = false;
const { label_atom_id, _rowCount: atomCount } = model.atomicHierarchy.atoms;
for (let i = 0; i < atomCount; ++i) {
const atomName = label_atom_id.value(i);
if (!hasBB && atomName === 'BB')
hasBB = true;
if (!hasSC1 && atomName === 'SC1')
hasSC1 = true;
if (hasBB && hasSC1)
break;
}
coarseGrained = false;
if (atomCount > 0 && polymerResidueCount > 0) {
if (hasBB && hasSC1) {
coarseGrained = true;
}
else if (atomCount / polymerResidueCount < 3) {
coarseGrained = true;
}
else if (polymerDirectionCount === 0) {
coarseGrained = true;
}
}
Model.CoarseGrained.set(model, coarseGrained);
}
return coarseGrained;
}
Model.isCoarseGrained = isCoarseGrained;
//
function hasCarbohydrate(model) {
return model.properties.saccharideComponentMap.size > 0;
}
Model.hasCarbohydrate = hasCarbohydrate;
function hasProtein(model) {
const { subtype } = model.entities;
for (let i = 0, il = subtype.rowCount; i < il; ++i) {
if (subtype.value(i).startsWith('polypeptide'))
return true;
}
return false;
}
Model.hasProtein = hasProtein;
function hasNucleic(model) {
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;
}
Model.hasNucleic = hasNucleic;
function isFromPdbArchive(model) {
if (!MmcifFormat.is(model.sourceData))
return false;
const { db } = model.sourceData.data;
for (let i = 0, il = db.database_2.database_id.rowCount; i < il; ++i) {
if (db.database_2.database_id.value(i) === 'pdb')
return true;
}
return false;
}
Model.isFromPdbArchive = isFromPdbArchive;
function hasPdbId(model) {
if (!MmcifFormat.is(model.sourceData))
return false;
return (
// 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);
}
Model.hasPdbId = hasPdbId;
function hasSecondaryStructure(model) {
if (MmcifFormat.is(model.sourceData)) {
const { db } = model.sourceData.data;
return (db.struct_conf.id.isDefined ||
db.struct_sheet_range.id.isDefined);
}
else {
return ModelSecondaryStructure.Provider.isApplicable(model);
}
}
Model.hasSecondaryStructure = hasSecondaryStructure;
const tmpAngles90 = Vec3.create(1.5707963, 1.5707963, 1.5707963); // in radians
const tmpLengths1 = Vec3.create(1, 1, 1);
function hasCrystalSymmetry(model) {
var _a;
const spacegroup = (_a = ModelSymmetry.Provider.get(model)) === null || _a === void 0 ? void 0 : _a.spacegroup;
return !!spacegroup && !(spacegroup.num === 1 &&
Vec3.equals(spacegroup.cell.anglesInRadians, tmpAngles90) &&
Vec3.equals(spacegroup.cell.size, tmpLengths1));
}
Model.hasCrystalSymmetry = hasCrystalSymmetry;
function isFromXray(model) {
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;
}
Model.isFromXray = isFromXray;
function isFromEm(model) {
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;
}
Model.isFromEm = isFromEm;
function isFromNmr(model) {
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;
}
Model.isFromNmr = isFromNmr;
function isExperimental(model) {
if (!MmcifFormat.is(model.sourceData))
return false;
const { db } = model.sourceData.data;
for (let i = 0; i < db.struct.pdbx_structure_determination_methodology.rowCount; i++) {
if (db.struct.pdbx_structure_determination_methodology.value(i).toLowerCase() === 'experimental')
return true;
}
return false;
}
Model.isExperimental = isExperimental;
function isIntegrative(model) {
if (!MmcifFormat.is(model.sourceData))
return false;
const { db } = model.sourceData.data;
for (let i = 0; i < db.struct.pdbx_structure_determination_methodology.rowCount; i++) {
if (db.struct.pdbx_structure_determination_methodology.value(i).toLowerCase() === 'integrative')
return true;
}
return false;
}
Model.isIntegrative = isIntegrative;
function isComputational(model) {
if (!MmcifFormat.is(model.sourceData))
return false;
const { db } = model.sourceData.data;
for (let i = 0; i < db.struct.pdbx_structure_determination_methodology.rowCount; i++) {
if (db.struct.pdbx_structure_determination_methodology.value(i).toLowerCase() === 'computational')
return true;
}
return false;
}
Model.isComputational = isComputational;
function hasXrayMap(model) {
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';
}
Model.hasXrayMap = hasXrayMap;
/**
* 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'.
*/
function hasEmMap(model) {
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;
}
Model.hasEmMap = hasEmMap;
function hasDensityMap(model) {
if (!MmcifFormat.is(model.sourceData))
return false;
return hasXrayMap(model) || hasEmMap(model);
}
Model.hasDensityMap = hasDensityMap;
function probablyHasDensityMap(model) {
if (!MmcifFormat.is(model.sourceData))
return false;
if (Model.isIntegrative(model))
return false;
const { db } = model.sourceData.data;
return hasDensityMap(model) || (
// check if from pdb archive but missing relevant meta data
hasPdbId(model) && (!db.exptl.method.isDefined ||
(isFromXray(model) && (!db.pdbx_database_status.status_code_sf.isDefined ||
db.pdbx_database_status.status_code_sf.valueKind(0) === 2 /* Column.ValueKinds.Unknown */)) ||
(isFromEm(model) && (!db.pdbx_database_related.db_name.isDefined))));
}
Model.probablyHasDensityMap = probablyHasDensityMap;
})(Model || (Model = {}));