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
No related branches found
No related tags found
No related merge requests found
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
......@@ -4,45 +4,57 @@
* @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 { 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 { Carbohydrates, CarbohydrateLink, CarbohydrateTerminalLink, CarbohydrateElement } from './data';
import Unit from '../unit';
import { SaccharideNameMap, UnknownSaccharideComponent } from './constants';
import { Vec3 } from 'mol-math/linear-algebra';
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';
import { CarbohydrateElement, CarbohydrateLink, Carbohydrates, CarbohydrateTerminalLink } from './data';
function getResidueIndex(elementIndex: number, unit: Unit.Atomic) {
return unit.model.atomicHierarchy.residueAtomSegments.index[unit.elements[elementIndex]]
}
function getRingIndices(unit: Unit.Atomic, rI: ResidueIndex) {
const { offsets } = unit.model.atomicHierarchy.residueAtomSegments
const { elements } = unit
const interval = Interval.ofBounds(offsets[rI], offsets[rI + 1])
const rings: number[] = []
rings.push(...unit.rings.byFingerprint.get('C-C-C-C-C-O') || [])
rings.push(...unit.rings.byFingerprint.get('C-C-C-C-O') || [])
const sugarRings: ReadonlyArray<number>[] = []
for (let i = 0, il = rings.length; i < il; ++i) {
let withinIntervalCount = 0
const ring = unit.rings.all[rings[i]]
for (let j = 0, jl = ring.length; j < jl; ++j) {
if (Interval.has(interval, elements[ring[j]])) ++withinIntervalCount
function sugarResidueIdx(unit: Unit.Atomic, ring: ArrayLike<StructureElement.UnitIndex>): ResidueIndex {
const { elements } = unit;
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index;
const idx = residueIndex[elements[ring[0]]];
for (let rI = 1, _rI = ring.length; rI < _rI; rI++) {
if (idx !== residueIndex[elements[ring[rI]]]) return -1 as ResidueIndex;
}
return idx;
}
function addSugarRings(unit: Unit.Atomic, fp: string, sugarResidues: Map<ResidueIndex, number[]>) {
const rings = unit.rings;
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')
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
const { elements } = unit
const { position } = unit.conformation
......@@ -108,6 +120,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
const chainIt = Segmentation.transientSegments(chainAtomSegments, unit.elements)
const residueIt = Segmentation.transientSegments(residueAtomSegments, unit.elements)
let sugarResidueMap: Map<ResidueIndex, number[]> | undefined = void 0;
while (chainIt.hasNext) {
residueIt.setSegment(chainIt.move());
......@@ -119,14 +133,27 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
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[] = []
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 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)
const elementIndex = elements.length
......@@ -138,8 +165,9 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
// add carbohydrate links induced by intra-residue bonds
const ringCombinations = combinations(fillSerial(new Array(sugarRings.length)), 2)
for (let j = 0, jl = ringCombinations.length; j < jl; ++j) {
const rc = ringCombinations[j]
if (areConnected(sugarRings[rc[0]], sugarRings[rc[1]], unit.links, 2)) {
const rc = ringCombinations[j];
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
fixLinkDirection(ringElements[rc[0]], ringElements[rc[1]])
fixLinkDirection(ringElements[rc[1]], ringElements[rc[0]])
......@@ -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