Skip to content
Snippets Groups Projects
Commit 96523e76 authored by David Sehnal's avatar David Sehnal
Browse files

Updated carbohydrate detection in mol-model

parent 40c73819
Branches
Tags
No related merge requests found
File suppressed by a .gitattributes entry, the file's encoding is unsupported, or the file size exceeds the limit.
...@@ -4,45 +4,57 @@ ...@@ -4,45 +4,57 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
import Unit from '../unit'; import { Segmentation } from 'mol-data/int';
import { combinations } from 'mol-data/util/combination';
import { areConnected } from 'mol-math/graph';
import { Vec3 } from 'mol-math/linear-algebra';
import PrincipalAxes from 'mol-math/linear-algebra/matrix/principal-axes';
import { fillSerial } from 'mol-util/array';
import { ResidueIndex } from '../../model'; import { ResidueIndex } from '../../model';
import { Interval, Segmentation } from 'mol-data/int'; import { ElementSymbol, MoleculeType } from '../../model/types';
import { getMoleculeType, getPositionMatrix } from '../../util';
import StructureElement from '../element';
import Structure from '../structure'; import Structure from '../structure';
import { Carbohydrates, CarbohydrateLink, CarbohydrateTerminalLink, CarbohydrateElement } from './data'; import Unit from '../unit';
import { SaccharideNameMap, UnknownSaccharideComponent } from './constants'; import { SaccharideNameMap, UnknownSaccharideComponent } from './constants';
import { Vec3 } from 'mol-math/linear-algebra'; import { CarbohydrateElement, CarbohydrateLink, Carbohydrates, CarbohydrateTerminalLink } from './data';
import { getMoleculeType, getPositionMatrix } from '../../util';
import { MoleculeType, ElementSymbol } from '../../model/types';
import { areConnected } from 'mol-math/graph';
import { combinations } from 'mol-data/util/combination';
import { fillSerial } from 'mol-util/array';
import PrincipalAxes from 'mol-math/linear-algebra/matrix/principal-axes';
function getResidueIndex(elementIndex: number, unit: Unit.Atomic) { function getResidueIndex(elementIndex: number, unit: Unit.Atomic) {
return unit.model.atomicHierarchy.residueAtomSegments.index[unit.elements[elementIndex]] return unit.model.atomicHierarchy.residueAtomSegments.index[unit.elements[elementIndex]]
} }
function getRingIndices(unit: Unit.Atomic, rI: ResidueIndex) { function sugarResidueIdx(unit: Unit.Atomic, ring: ArrayLike<StructureElement.UnitIndex>): ResidueIndex {
const { offsets } = unit.model.atomicHierarchy.residueAtomSegments const { elements } = unit;
const { elements } = unit const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index;
const interval = Interval.ofBounds(offsets[rI], offsets[rI + 1]) const idx = residueIndex[elements[ring[0]]];
const rings: number[] = [] for (let rI = 1, _rI = ring.length; rI < _rI; rI++) {
rings.push(...unit.rings.byFingerprint.get('C-C-C-C-C-O') || []) if (idx !== residueIndex[elements[ring[rI]]]) return -1 as ResidueIndex;
rings.push(...unit.rings.byFingerprint.get('C-C-C-C-O') || []) }
const sugarRings: ReadonlyArray<number>[] = [] return idx;
for (let i = 0, il = rings.length; i < il; ++i) { }
let withinIntervalCount = 0
const ring = unit.rings.all[rings[i]] function addSugarRings(unit: Unit.Atomic, fp: string, sugarResidues: Map<ResidueIndex, number[]>) {
for (let j = 0, jl = ring.length; j < jl; ++j) { const rings = unit.rings;
if (Interval.has(interval, elements[ring[j]])) ++withinIntervalCount const byFp = rings.byFingerprint.get(fp);
if (!byFp) return;
for (const r of byFp) {
const idx = sugarResidueIdx(unit, rings.all[r]);
if (idx >= 0) {
if (sugarResidues.has(idx)) sugarResidues.get(idx)!.push(r);
else sugarResidues.set(idx, [r]);
} }
if (withinIntervalCount === ring.length) sugarRings.push(ring)
} }
return sugarRings }
function getSugarRingIndices(unit: Unit.Atomic) {
const sugarResidues = new Map<ResidueIndex, number[]>();
addSugarRings(unit, 'C-C-C-C-C-O', sugarResidues);
addSugarRings(unit, 'C-C-C-C-O', sugarResidues);
return sugarResidues;
} }
const C = ElementSymbol('C') const C = ElementSymbol('C')
function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ReadonlyArray<number>, center: Vec3) { function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ReadonlyArray<StructureElement.UnitIndex>, center: Vec3) {
let indexC1 = -1, indexC1X = -1, indexC = -1 let indexC1 = -1, indexC1X = -1, indexC = -1
const { elements } = unit const { elements } = unit
const { position } = unit.conformation const { position } = unit.conformation
...@@ -108,6 +120,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ...@@ -108,6 +120,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
const chainIt = Segmentation.transientSegments(chainAtomSegments, unit.elements) const chainIt = Segmentation.transientSegments(chainAtomSegments, unit.elements)
const residueIt = Segmentation.transientSegments(residueAtomSegments, unit.elements) const residueIt = Segmentation.transientSegments(residueAtomSegments, unit.elements)
let sugarResidueMap: Map<ResidueIndex, number[]> | undefined = void 0;
while (chainIt.hasNext) { while (chainIt.hasNext) {
residueIt.setSegment(chainIt.move()); residueIt.setSegment(chainIt.move());
...@@ -119,14 +133,27 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ...@@ -119,14 +133,27 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
if (getMoleculeType(unit.model, residueIndex) !== MoleculeType.saccharide) continue if (getMoleculeType(unit.model, residueIndex) !== MoleculeType.saccharide) continue
} }
const sugarRings = getRingIndices(unit, residueIndex) if (!sugarResidueMap) {
sugarResidueMap = getSugarRingIndices(unit);
}
const sugarRings = sugarResidueMap.get(residueIndex);
if (!sugarRings || !sugarRings.length) {
console.warn(`No ring found for carbohydrate on residue with index ${residueIndex}, unit ${unit.id}. Residue skipped.`);
continue;
}
const rings = unit.rings;
const ringElements: number[] = [] const ringElements: number[] = []
for (let j = 0, jl = sugarRings.length; j < jl; ++j) { for (let j = 0, jl = sugarRings.length; j < jl; ++j) {
const pa = new PrincipalAxes(getPositionMatrix(unit, sugarRings[j])) const ringAtoms = rings.all[sugarRings[j]];
const pa = new PrincipalAxes(getPositionMatrix(unit, ringAtoms))
const center = Vec3.copy(Vec3.zero(), pa.center) const center = Vec3.copy(Vec3.zero(), pa.center)
const normal = Vec3.copy(Vec3.zero(), pa.normVecC) const normal = Vec3.copy(Vec3.zero(), pa.normVecC)
const direction = getDirection(Vec3.zero(), unit, sugarRings[j], center) const direction = getDirection(Vec3.zero(), unit, ringAtoms, center)
Vec3.orthogonalize(direction, normal, direction) Vec3.orthogonalize(direction, normal, direction)
const elementIndex = elements.length const elementIndex = elements.length
...@@ -138,8 +165,9 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ...@@ -138,8 +165,9 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
// add carbohydrate links induced by intra-residue bonds // add carbohydrate links induced by intra-residue bonds
const ringCombinations = combinations(fillSerial(new Array(sugarRings.length)), 2) const ringCombinations = combinations(fillSerial(new Array(sugarRings.length)), 2)
for (let j = 0, jl = ringCombinations.length; j < jl; ++j) { for (let j = 0, jl = ringCombinations.length; j < jl; ++j) {
const rc = ringCombinations[j] const rc = ringCombinations[j];
if (areConnected(sugarRings[rc[0]], sugarRings[rc[1]], unit.links, 2)) { const r0 = rings.all[sugarRings[rc[0]]], r1 = rings.all[sugarRings[rc[1]]];
if (areConnected(r0, r1, unit.links, 2)) {
// fix both directions as it is unlcear where the C1 atom is // fix both directions as it is unlcear where the C1 atom is
fixLinkDirection(ringElements[rc[0]], ringElements[rc[1]]) fixLinkDirection(ringElements[rc[0]], ringElements[rc[1]])
fixLinkDirection(ringElements[rc[1]], ringElements[rc[0]]) fixLinkDirection(ringElements[rc[1]], ringElements[rc[0]])
...@@ -153,10 +181,6 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ...@@ -153,10 +181,6 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
}) })
} }
} }
if (!sugarRings.length) {
console.warn('No ring found for carbohydrate')
}
} }
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment