molstar
Version:
A comprehensive macromolecular library.
277 lines (276 loc) • 12.7 kB
JavaScript
/**
* Copyright (c) 2021-24 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 { toDatabase } from '../../../mol-io/reader/cif/schema';
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
import { Unit } from '../../../mol-model/structure';
import { CustomPropSymbol } from '../../../mol-script/language/symbol';
import { Type } from '../../../mol-script/language/type';
import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
import { ParamDefinition, ParamDefinition as PD } from '../../../mol-util/param-definition';
export { QualityAssessment };
var QualityAssessment;
(function (QualityAssessment) {
const Empty = {
value: {
local: [],
localMap: new Map(),
localMetrics: new Map(),
}
};
function isApplicable(model, localMetricName) {
if (!model || !MmcifFormat.is(model.sourceData))
return false;
const { db } = model.sourceData.data;
const hasLocalMetric = (db.ma_qa_metric.id.isDefined &&
db.ma_qa_metric_local.ordinal_id.isDefined);
if (localMetricName && hasLocalMetric) {
const nameQuery = localMetricName.toLowerCase();
for (let i = 0, il = db.ma_qa_metric._rowCount; i < il; i++) {
if (db.ma_qa_metric.mode.value(i) !== 'local')
continue;
const name = db.ma_qa_metric.name.value(i).toLowerCase();
if (name.includes(nameQuery))
return true;
}
return false;
}
else {
return hasLocalMetric;
}
}
QualityAssessment.isApplicable = isApplicable;
function getLocalOptions(model, kind) {
var _a, _b;
if (!model)
return ParamDefinition.Select(undefined, [], { label: 'Metric', isHidden: true });
const local = (_a = QualityAssessmentProvider.get(model).value) === null || _a === void 0 ? void 0 : _a.local;
if (!local)
return ParamDefinition.Select(undefined, [], { label: 'Metric', isHidden: true });
const options = local.filter(m => m.kind === kind).map(m => [m.id, `${m.name} (${m.type})`]);
return ParamDefinition.Select((_b = options[0]) === null || _b === void 0 ? void 0 : _b[0], options, { label: 'Metric ' });
}
QualityAssessment.getLocalOptions = getLocalOptions;
async function obtain(ctx, model, props) {
var _a, _b;
if (!model || !MmcifFormat.is(model.sourceData))
return Empty;
const { ma_qa_metric, ma_qa_metric_local } = model.sourceData.data.db;
const { model_id, label_asym_id, label_seq_id, metric_id, metric_value } = ma_qa_metric_local;
const { index } = model.atomicHierarchy;
const locals = [];
const localMetrics = new Map();
// for simplicity we assume names in ma_qa_metric for mode 'local' are unique
const localMetricValues = new Map();
const localNames = new Map();
for (let i = 0, il = ma_qa_metric._rowCount; i < il; i++) {
if (ma_qa_metric.mode.value(i) !== 'local')
continue;
const id = ma_qa_metric.id.value(i);
const name = ma_qa_metric.name.value(i);
const type = ma_qa_metric.type.value(i);
const values = new Map();
const ispPLDDType = type.toLowerCase().includes('plddt');
const has01Range = type.replace(/\s/g, '').includes('[0,1]');
let domain;
if (has01Range) {
domain = [0, 1];
}
else if (ispPLDDType) {
domain = [0, 100];
}
let kind;
const nameLower = name.toLowerCase();
if (nameLower.includes('plddt'))
kind = 'pLDDT';
else if (nameLower.includes('qmean'))
kind = 'qmean';
if (!kind && ispPLDDType)
kind = 'pLDDT';
if (!kind && has01Range)
kind = 'qmean';
const metric = { id, kind, type, name, domain, valueRange: [Number.MAX_VALUE, -Number.MAX_VALUE], values };
localMetrics.set(id, metric);
locals.push(metric);
if (localMetricValues.has(name)) {
console.warn(`local ma_qa_metric with name '${name}' already added`);
continue;
}
localMetricValues.set(name, values);
localNames.set(id, name);
}
const residueKey = {
label_entity_id: '',
label_asym_id: '',
label_seq_id: 0,
pdbx_PDB_ins_code: undefined,
};
for (let i = 0, il = ma_qa_metric_local._rowCount; i < il; i++) {
if (model_id.value(i) !== model.modelNum)
continue;
const labelAsymId = label_asym_id.value(i);
const entityIndex = index.findEntity(labelAsymId);
residueKey.label_entity_id = model.entities.data.id.value(entityIndex);
residueKey.label_asym_id = labelAsymId;
residueKey.label_seq_id = label_seq_id.value(i);
const rI = index.findResidueLabel(residueKey);
if (rI >= 0) {
const entry = localMetrics.get(metric_id.value(i));
if (!entry)
continue;
const value = metric_value.value(i);
const range = entry.valueRange;
if (value < range[0])
range[0] = value;
if (value > range[1])
range[1] = value;
entry.values.set(rI, value);
}
}
return {
value: {
local: Array.from(localMetrics.values()),
localMap: localMetrics,
pLDDT: (_a = locals.find(m => m.kind === 'pLDDT')) === null || _a === void 0 ? void 0 : _a.values,
qmean: (_b = locals.find(m => m.kind === 'qmean')) === null || _b === void 0 ? void 0 : _b.values,
localMetrics: localMetricValues,
}
};
}
QualityAssessment.obtain = obtain;
const PairwiseSchema = {
ma_qa_metric: mmCIF_Schema.ma_qa_metric,
ma_qa_metric_local_pairwise: mmCIF_Schema.ma_qa_metric_local_pairwise
};
function findModelArchiveCIFPAEMetrics(frame) {
const { ma_qa_metric, ma_qa_metric_local_pairwise } = toDatabase(PairwiseSchema, frame);
const result = [];
if (ma_qa_metric_local_pairwise._rowCount === 0)
return result;
for (let i = 0, il = ma_qa_metric._rowCount; i < il; i++) {
if (ma_qa_metric.mode.value(i) !== 'local-pairwise')
continue;
const id = ma_qa_metric.id.value(i);
const name = ma_qa_metric.name.value(i);
if (!name.toLowerCase().includes('pae'))
continue;
result.push({ id, name });
}
return result;
}
QualityAssessment.findModelArchiveCIFPAEMetrics = findModelArchiveCIFPAEMetrics;
function pairwiseMetricFromModelArchiveCIF(model, frame, metricId) {
const db = toDatabase(PairwiseSchema, frame);
if (!db.ma_qa_metric_local_pairwise._rowCount)
return undefined;
const { ma_qa_metric, ma_qa_metric_local_pairwise } = db;
const { model_id, label_asym_id_1, label_seq_id_1, label_asym_id_2, label_seq_id_2, metric_id, metric_value } = db.ma_qa_metric_local_pairwise;
const { index } = model.atomicHierarchy;
let metric;
for (let i = 0, il = ma_qa_metric._rowCount; i < il; i++) {
if (ma_qa_metric.mode.value(i) !== 'local-pairwise')
continue;
const id = ma_qa_metric.id.value(i);
if (id !== metricId)
continue;
const name = ma_qa_metric.name.value(i);
metric = {
id,
name,
residueRange: [Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER],
valueRange: [Number.MAX_VALUE, -Number.MAX_VALUE],
values: {}
};
}
if (!metric)
return undefined;
const { values, residueRange, valueRange } = metric;
const residueKey = {
label_entity_id: '',
label_asym_id: '',
label_seq_id: 0,
pdbx_PDB_ins_code: undefined,
};
for (let i = 0, il = ma_qa_metric_local_pairwise._rowCount; i < il; i++) {
if (model_id.value(i) !== model.modelNum || metric_id.value(i) !== metricId)
continue;
let labelAsymId = label_asym_id_1.value(i);
let entityIndex = index.findEntity(labelAsymId);
residueKey.label_entity_id = model.entities.data.id.value(entityIndex);
residueKey.label_asym_id = labelAsymId;
residueKey.label_seq_id = label_seq_id_1.value(i);
const rI_1 = index.findResidueLabel(residueKey);
if (rI_1 < 0)
continue;
labelAsymId = label_asym_id_2.value(i);
entityIndex = index.findEntity(labelAsymId);
residueKey.label_entity_id = model.entities.data.id.value(entityIndex);
residueKey.label_asym_id = labelAsymId;
residueKey.label_seq_id = label_seq_id_2.value(i);
const rI_2 = index.findResidueLabel(residueKey);
if (rI_1 < 0)
continue;
let r1 = values[rI_1];
if (!r1) {
r1 = {};
values[rI_1] = r1;
}
const value = metric_value.value(i);
r1[rI_2] = value;
if (rI_1 < residueRange[0])
residueRange[0] = rI_1;
if (rI_2 < residueRange[0])
residueRange[0] = rI_2;
if (rI_1 > residueRange[1])
residueRange[1] = rI_1;
if (rI_2 > residueRange[1])
residueRange[1] = rI_2;
if (value < valueRange[0])
valueRange[0] = value;
if (value > valueRange[1])
valueRange[1] = value;
}
return metric;
}
QualityAssessment.pairwiseMetricFromModelArchiveCIF = pairwiseMetricFromModelArchiveCIF;
QualityAssessment.symbols = {
pLDDT: QuerySymbolRuntime.Dynamic(CustomPropSymbol('ma', 'quality-assessment.pLDDT', Type.Num), ctx => {
var _a, _b;
const { unit, element } = ctx.element;
if (!Unit.isAtomic(unit))
return -1;
const qualityAssessment = QualityAssessmentProvider.get(unit.model).value;
return (_b = (_a = qualityAssessment === null || qualityAssessment === void 0 ? void 0 : qualityAssessment.pLDDT) === null || _a === void 0 ? void 0 : _a.get(unit.model.atomicHierarchy.residueAtomSegments.index[element])) !== null && _b !== void 0 ? _b : -1;
}),
qmean: QuerySymbolRuntime.Dynamic(CustomPropSymbol('ma', 'quality-assessment.qmean', Type.Num), ctx => {
var _a, _b;
const { unit, element } = ctx.element;
if (!Unit.isAtomic(unit))
return -1;
const qualityAssessment = QualityAssessmentProvider.get(unit.model).value;
return (_b = (_a = qualityAssessment === null || qualityAssessment === void 0 ? void 0 : qualityAssessment.qmean) === null || _a === void 0 ? void 0 : _a.get(unit.model.atomicHierarchy.residueAtomSegments.index[element])) !== null && _b !== void 0 ? _b : -1;
}),
};
})(QualityAssessment || (QualityAssessment = {}));
export const QualityAssessmentParams = {};
export const QualityAssessmentProvider = CustomModelProperty.createProvider({
label: 'QualityAssessment',
descriptor: CustomPropertyDescriptor({
name: 'ma_quality_assessment',
symbols: QualityAssessment.symbols
}),
type: 'static',
defaultParams: QualityAssessmentParams,
getParams: (data) => QualityAssessmentParams,
isApplicable: (data) => QualityAssessment.isApplicable(data),
obtain: async (ctx, data, props) => {
const p = { ...PD.getDefaultValues(QualityAssessmentParams), ...props };
return await QualityAssessment.obtain(ctx, data, p);
}
});