From 31d387390546ca49775096e88b3d626faa267a59 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Fri, 19 Apr 2019 15:57:41 -0700
Subject: [PATCH] split dssp calculation into multiple files

---
 .../computed/secondary-structure/dssp.ts      | 698 +-----------------
 .../dssp/backbone-hbonds.ts                   | 151 ++++
 .../dssp/backbone-indices.ts                  |  36 +
 .../secondary-structure/dssp/bends.ts         |  69 ++
 .../secondary-structure/dssp/bridges.ts       |  77 ++
 .../secondary-structure/dssp/common.ts        |  93 +++
 .../dssp/dihedral-angles.ts                   |  61 ++
 .../secondary-structure/dssp/helices.ts       |  63 ++
 .../secondary-structure/dssp/ladders.ts       | 101 +++
 .../secondary-structure/dssp/sheets.ts        |  56 ++
 .../secondary-structure/dssp/trace-lookup.ts  |  28 +
 .../secondary-structure/dssp/turns.ts         |  51 ++
 12 files changed, 801 insertions(+), 683 deletions(-)
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/backbone-hbonds.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/backbone-indices.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/bends.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/bridges.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/common.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/dihedral-angles.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/helices.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/ladders.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/sheets.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/trace-lookup.ts
 create mode 100644 src/mol-model-props/computed/secondary-structure/dssp/turns.ts

diff --git a/src/mol-model-props/computed/secondary-structure/dssp.ts b/src/mol-model-props/computed/secondary-structure/dssp.ts
index af16b2814..b2cdf1d8e 100644
--- a/src/mol-model-props/computed/secondary-structure/dssp.ts
+++ b/src/mol-model-props/computed/secondary-structure/dssp.ts
@@ -6,17 +6,20 @@
  */
 
 import { SecondaryStructure } from 'mol-model/structure/model/properties/seconday-structure';
-import { ResidueIndex } from 'mol-model/structure';
-import { MoleculeType, SecondaryStructureType } from 'mol-model/structure/model/types';
-import { Vec3 } from 'mol-math/linear-algebra';
-import { GridLookup3D } from 'mol-math/geometry';
-import { SortedArray } from 'mol-data/int';
-import { IntAdjacencyGraph } from 'mol-math/graph';
-import { BitFlags } from 'mol-util';
-import { ElementIndex } from 'mol-model/structure/model/indexing';
+import { SecondaryStructureType } from 'mol-model/structure/model/types';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
-import { radToDeg } from 'mol-math/misc';
 import { AtomicHierarchy, AtomicConformation } from 'mol-model/structure/model/properties/atomic';
+import { assignBends } from './dssp/bends';
+import { calcBackboneHbonds } from './dssp/backbone-hbonds';
+import { Ladder, Bridge, DSSPContext, DSSPType } from './dssp/common';
+import { assignTurns } from './dssp/turns';
+import { assignHelices } from './dssp/helices';
+import { assignLadders } from './dssp/ladders';
+import { assignBridges } from './dssp/bridges';
+import { assignSheets } from './dssp/sheets';
+import { calculateDihedralAngles } from './dssp/dihedral-angles';
+import { calcBackboneAtomIndices } from './dssp/backbone-indices';
+import { calcAtomicTraceLookup3D } from './dssp/trace-lookup';
 
 /**
  * TODO bugs to fix:
@@ -26,93 +29,6 @@ import { AtomicHierarchy, AtomicConformation } from 'mol-model/structure/model/p
  * - validate new ordering of secondary structure elements
  */
 
- /** max distance between two C-alpha atoms to check for hbond */
-const caMaxDist = 9.0;
-
-/**
- * Constant for electrostatic energy in kcal/mol
- *      f  *  q1 *   q2
- * Q = -332 * 0.42 * 0.20
- *
- * f is the dimensional factor
- *
- * q1 and q2 are partial charges which are placed on the C,O
- * (+q1,-q1) and N,H (-q2,+q2)
- */
-const Q = -27.888
-
-/** cutoff for hbonds in kcal/mol, must be lower to be consider as an hbond */
-const hbondEnergyCutoff = -0.5
-/** prevent extremely low hbond energies */
-const hbondEnergyMinimal = -9.9
-
-interface DSSPContext {
-    params: Partial<PD.Values<DSSPComputationParams>>,
-    getResidueFlag: (f: DSSPType) => SecondaryStructureType,
-    getFlagName: (f: DSSPType) => String,
-
-    hierarchy: AtomicHierarchy
-    proteinResidues: SortedArray<ResidueIndex>
-    /** flags for each residue */
-    flags: Uint32Array
-    hbonds: DsspHbonds,
-
-    torsionAngles: { phi: Float32Array, psi: Float32Array },
-    backboneIndices: BackboneAtomIndices,
-    conformation: AtomicConformation,
-    ladders: Ladder[],
-    bridges: Bridge[]
-}
-
-interface Ladder {
-    previousLadder: number,
-    nextLadder: number,
-    firstStart: number,
-    secondStart: number,
-    secondEnd: number,
-    firstEnd: number,
-    type: BridgeType
-}
-
-const enum BridgeType {
-    PARALLEL = 0x0,
-    ANTI_PARALLEL = 0x1
-}
-
-class Bridge {
-    partner1: number;
-    partner2: number;
-    type: BridgeType;
-
-    constructor(p1: number, p2: number, type: BridgeType) {
-        this.partner1 = Math.min(p1, p2)
-        this.partner2 = Math.max(p1, p2)
-        this.type = type
-    }
-}
-
-type DSSPType = BitFlags<DSSPType.Flag>
-namespace DSSPType {
-    export const is: (t: DSSPType, f: Flag) => boolean = BitFlags.has
-    export const create: (f: Flag) => DSSPType = BitFlags.create
-    export const enum Flag {
-        _ = 0x0,
-        H = 0x1,
-        B = 0x2,
-        E = 0x4,
-        G = 0x8,
-        I = 0x10,
-        S = 0x20,
-        T = 0x40,
-        T3 = 0x80,
-        T4 = 0x100,
-        T5 = 0x200,
-        T3S = 0x400, // marks 3-turn start
-        T4S = 0x800,
-        T5S = 0x1000
-    }
-}
-
 export const DSSPComputationParams = {
     oldDefinition: PD.Boolean(true, { description: 'Whether to use the old DSSP convention for the annotation of turns and helices, causes them to be two residues shorter' }),
     oldOrdering: PD.Boolean(true, { description: 'Alpha-helices are preferred over 3-10 helices' })
@@ -120,10 +36,8 @@ export const DSSPComputationParams = {
 export type DSSPComputationParams = typeof DSSPComputationParams
 export type DSSPComputationProps = PD.Values<DSSPComputationParams>
 
-export function computeModelDSSP(hierarchy: AtomicHierarchy,
-    conformation: AtomicConformation,
-    params: Partial<DSSPComputationProps> = {}): SecondaryStructure {
-    params = { ...PD.getDefaultValues(DSSPComputationParams), ...params };
+export function computeModelDSSP(hierarchy: AtomicHierarchy, conformation: AtomicConformation, params: Partial<DSSPComputationProps> = {}): SecondaryStructure {
+    const p = { ...PD.getDefaultValues(DSSPComputationParams), ...params };
 
     const { lookup3d, proteinResidues } = calcAtomicTraceLookup3D(hierarchy, conformation)
     const backboneIndices = calcBackboneAtomIndices(hierarchy, proteinResidues)
@@ -143,7 +57,7 @@ export function computeModelDSSP(hierarchy: AtomicHierarchy,
     const getFlagName = params.oldOrdering ? getOriginalFlagName : getUpdatedFlagName
 
     const ctx: DSSPContext = {
-        params,
+        params: p,
         getResidueFlag,
         getFlagName,
 
@@ -223,261 +137,6 @@ function mapToKind(assignment: SecondaryStructureType.Flag) {
     }
 }
 
-function calcAtomicTraceLookup3D(hierarchy: AtomicHierarchy, conformation: AtomicConformation) {
-    const { x, y, z } = conformation;
-    const { moleculeType, traceElementIndex } = hierarchy.derived.residue
-    const indices: number[] = []
-    const _proteinResidues: number[] = []
-    for (let i = 0, il = moleculeType.length; i < il; ++i) {
-        if (moleculeType[i] === MoleculeType.protein) {
-            indices[indices.length] = traceElementIndex[i]
-            _proteinResidues[_proteinResidues.length] = i
-        }
-    }
-    const lookup3d = GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(indices) }, 4);
-    const proteinResidues = SortedArray.ofSortedArray<ResidueIndex>(_proteinResidues)
-    return { lookup3d, proteinResidues }
-}
-
-interface BackboneAtomIndices {
-    cIndices: ArrayLike<ElementIndex | -1>
-    hIndices: ArrayLike<ElementIndex | -1>
-    oIndices: ArrayLike<ElementIndex | -1>
-    nIndices: ArrayLike<ElementIndex | -1>
-}
-
-function calcBackboneAtomIndices(hierarchy: AtomicHierarchy, proteinResidues: SortedArray<ResidueIndex>): BackboneAtomIndices {
-    const residueCount = proteinResidues.length
-    const { index } = hierarchy
-
-    const c = new Int32Array(residueCount)
-    const h = new Int32Array(residueCount)
-    const o = new Int32Array(residueCount)
-    const n = new Int32Array(residueCount)
-
-    for (let i = 0, il = residueCount; i < il; ++i) {
-        const rI = proteinResidues[i]
-        c[i] = index.findAtomOnResidue(rI, 'C')
-        h[i] = index.findAtomOnResidue(rI, 'H')
-        o[i] = index.findAtomOnResidue(rI, 'O')
-        n[i] = index.findAtomOnResidue(rI, 'N')
-    }
-
-    return {
-        cIndices: c as unknown as ArrayLike<ElementIndex | -1>,
-        hIndices: h as unknown as ArrayLike<ElementIndex | -1>,
-        oIndices: o as unknown as ArrayLike<ElementIndex | -1>,
-        nIndices: n as unknown as ArrayLike<ElementIndex | -1>,
-    }
-}
-
-type DsspHbonds = IntAdjacencyGraph<{ readonly energies: ArrayLike<number> }>
-
-/**
- * Bend(i) =: [angle ((CW - Ca(i - 2)),(C"(i + 2) - C"(i))) > 70"]
- *
- * Type: S
- */
-function assignBends(ctx: DSSPContext) {
-    const flags = ctx.flags
-    const { x, y, z } = ctx.conformation
-    const { traceElementIndex } = ctx.hierarchy.derived.residue
-
-    const proteinResidues = ctx.proteinResidues
-    const residueCount = proteinResidues.length
-
-    const position = (i: number, v: Vec3) => Vec3.set(v, x[i], y[i], z[i])
-
-    const caPosPrev2 = Vec3()
-    const caPos = Vec3()
-    const caPosNext2 = Vec3()
-
-    const nIndices = ctx.backboneIndices.nIndices
-    const cPos = Vec3()
-    const nPosNext = Vec3()
-
-    const caMinus2 = Vec3()
-    const caPlus2 = Vec3()
-
-    f1: for (let i = 2; i < residueCount - 2; i++) {
-        // check for peptide bond
-        for (let k = 0; k < 4; k++) {
-            let index = i + k - 2
-            position(traceElementIndex[index], cPos)
-            position(nIndices[index + 1], nPosNext)
-            if (Vec3.squaredDistance(cPos, nPosNext) > 6.25 /* max squared peptide bond distance allowed */) {
-                continue f1
-            }
-        }
-
-        const oRIprev2 = proteinResidues[i - 2]
-        const oRI = proteinResidues[i]
-        const oRInext2 = proteinResidues[i + 2]
-
-        const caAtomPrev2 = traceElementIndex[oRIprev2]
-        const caAtom = traceElementIndex[oRI]
-        const caAtomNext2 = traceElementIndex[oRInext2]
-
-        position(caAtomPrev2, caPosPrev2)
-        position(caAtom, caPos)
-        position(caAtomNext2, caPosNext2)
-
-        Vec3.sub(caMinus2, caPosPrev2, caPos)
-        Vec3.sub(caPlus2, caPos, caPosNext2)
-
-        const angle = radToDeg(Vec3.angle(caMinus2, caPlus2))
-        if (angle && angle > 70.00) {
-            flags[i] |= DSSPType.Flag.S
-        }
-    }
-}
-
-function calculateDihedralAngles(hierarchy: AtomicHierarchy, conformation: AtomicConformation, proteinResidues: SortedArray<ResidueIndex>, backboneIndices: BackboneAtomIndices): { phi: Float32Array, psi: Float32Array } {
-    const { cIndices, nIndices } = backboneIndices
-    const { index } = hierarchy
-    const { x, y, z } = conformation
-    const { traceElementIndex } = hierarchy.derived.residue
-
-    const residueCount = proteinResidues.length
-    const position = (i: number, v: Vec3) => i === -1 ? Vec3.setNaN(v) : Vec3.set(v, x[i], y[i], z[i])
-
-    let cPosPrev = Vec3(), caPosPrev = Vec3(), nPosPrev = Vec3()
-    let cPos = Vec3(), caPos = Vec3(), nPos = Vec3()
-    let cPosNext = Vec3(), caPosNext = Vec3(), nPosNext = Vec3()
-
-    if (residueCount === 0) return { phi: new Float32Array(0), psi: new Float32Array(0) }
-
-    const phi: Float32Array = new Float32Array(residueCount - 1)
-    const psi: Float32Array = new Float32Array(residueCount - 1)
-
-    position(-1, cPosPrev)
-    position(-1, caPosPrev)
-    position(-1, nPosPrev)
-
-    position(cIndices[0], cPos)
-    position(traceElementIndex[proteinResidues[0]], caPos)
-    position(nIndices[0], nPos)
-
-    position(cIndices[1], cPosNext)
-    position(traceElementIndex[proteinResidues[1]], caPosNext)
-    position(nIndices[1], nPosNext)
-
-    for (let i = 0; i < residueCount - 1; ++i) {
-        // ignore C-terminal residue as acceptor
-        if (index.findAtomOnResidue(proteinResidues[i], 'OXT') !== -1) continue
-
-        // returns NaN for missing atoms
-        phi[i] = Vec3.dihedralAngle(cPosPrev, nPos, caPos, cPos)
-        psi[i] = Vec3.dihedralAngle(nPos, caPos, cPos, nPosNext)
-
-        cPosPrev = cPos, caPosPrev = caPos, nPosPrev = nPos
-        cPos = cPosNext, caPos = caPosNext, nPos = nPosNext
-
-        position(cIndices[i + 1], cPosNext)
-        position(traceElementIndex[proteinResidues[i + 1]], caPosNext)
-        position(nIndices[i + 1], nPosNext)
-    }
-
-    return { phi, psi };
-}
-
-function calcBackboneHbonds(hierarchy: AtomicHierarchy, conformation: AtomicConformation, proteinResidues: SortedArray<ResidueIndex>, backboneIndices: BackboneAtomIndices, lookup3d: GridLookup3D): DsspHbonds {
-    const { cIndices, hIndices, nIndices, oIndices } = backboneIndices
-    const { index } = hierarchy
-    const { x, y, z } = conformation
-    const { traceElementIndex } = hierarchy.derived.residue
-
-    const residueCount = proteinResidues.length
-    const position = (i: number, v: Vec3) => Vec3.set(v, x[i], y[i], z[i])
-
-    const oAtomResidues: number[] = [];
-    const nAtomResidues: number[] = [];
-    const energies: number[] = [];
-
-    const oPos = Vec3()
-    const cPos = Vec3()
-    const caPos = Vec3()
-    const nPos = Vec3()
-    const hPos = Vec3()
-
-    const cPosPrev = Vec3()
-    const oPosPrev = Vec3()
-
-    for (let i = 0, il = proteinResidues.length; i < il; ++i) {
-        const oPI = i
-        const oRI = proteinResidues[i]
-
-        const oAtom = oIndices[oPI]
-        const cAtom = cIndices[oPI]
-        const caAtom = traceElementIndex[oRI]
-
-        // continue if residue is missing O or C atom
-        if (oAtom === -1 || cAtom === -1) continue
-
-        // ignore C-terminal residue as acceptor
-        if (index.findAtomOnResidue(oRI, 'OXT') !== -1) continue
-
-        position(oAtom, oPos)
-        position(cAtom, cPos)
-        position(caAtom, caPos)
-
-        const { indices, count } = lookup3d.find(caPos[0], caPos[1], caPos[2], caMaxDist)
-
-        for (let j = 0; j < count; ++j) {
-            const nPI = indices[j]
-
-            // ignore bonds within a residue or to prev or next residue, TODO take chain border into account
-            if (nPI === oPI || nPI - 1 === oPI || nPI + 1 === oPI) continue
-
-            const nAtom = nIndices[nPI]
-            if (nAtom === -1) continue
-
-            position(nAtom, nPos)
-
-            const hAtom = hIndices[nPI]
-            if (hAtom === -1) {
-                // approximate calculation of H position, TODO factor out
-                if (nPI === 0) continue
-                const nPIprev = nPI - 1
-
-                const oAtomPrev = oIndices[nPIprev]
-                const cAtomPrev = cIndices[nPIprev]
-                if (oAtomPrev === -1 || cAtomPrev === -1) continue
-
-                position(oAtomPrev, oPosPrev)
-                position(cAtomPrev, cPosPrev)
-
-                Vec3.sub(hPos, cPosPrev, oPosPrev)
-                const dist = Vec3.distance(oPosPrev, cPosPrev)
-                Vec3.scaleAndAdd(hPos, nPos, hPos, 1 / dist)
-            } else {
-                position(hAtom, hPos)
-            }
-
-            const e = calcHbondEnergy(oPos, cPos, nPos, hPos)
-            if (e > hbondEnergyCutoff) continue
-
-            oAtomResidues[oAtomResidues.length] = oPI
-            nAtomResidues[nAtomResidues.length] = nPI
-            energies[energies.length] = e
-        }
-    }
-
-    return buildHbondGraph(residueCount, oAtomResidues, nAtomResidues, energies);
-}
-
-function buildHbondGraph(residueCount: number, oAtomResidues: number[], nAtomResidues: number[], energies: number[]) {
-    const builder = new IntAdjacencyGraph.DirectedEdgeBuilder(residueCount, oAtomResidues, nAtomResidues);
-    const _energies = new Float32Array(builder.slotCount);
-
-    for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
-        builder.addNextEdge();
-        builder.assignProperty(_energies, energies[i]);
-    }
-
-    return builder.createGraph({ energies });
-}
-
 /** Original priority: H,B,E,G,I,T,S */
 function getOriginalResidueFlag(f: DSSPType) {
     if (DSSPType.is(f, DSSPType.Flag.H)) return SecondaryStructureType.SecondaryStructureDssp.H
@@ -532,331 +191,4 @@ function getDSSPAssignment(flags: Uint32Array, getResidueFlag: (f: DSSPType) =>
     }
 
     return type as unknown as ArrayLike<SecondaryStructureType>
-}
-
-/**
- * E = Q * (1/r(ON) + l/r(CH) - l/r(OH) - l/r(CN))
- */
-function calcHbondEnergy(oPos: Vec3, cPos: Vec3, nPos: Vec3, hPos: Vec3) {
-    const distOH = Vec3.distance(oPos, hPos)
-    const distCH = Vec3.distance(cPos, hPos)
-    const distCN = Vec3.distance(cPos, nPos)
-    const distON = Vec3.distance(oPos, nPos)
-
-    const e1 = Q / distOH - Q / distCH
-    const e2 = Q / distCN - Q / distON
-    const e = e1 + e2
-
-    // cap lowest possible energy
-    if (e < hbondEnergyMinimal)
-        return hbondEnergyMinimal
-
-    return e
-}
-
-/**
- * The basic turn pattern is a single H bond of type (i, i + n).
- * We assign an n-turn at residue i if there is an H bond from CO(i) to NH(i + n),
- * i.e., “n-turn(i)=: Hbond(i, i + n), n = 3, 4, 5.”
- *
- * Type: T
- */
-function assignTurns(ctx: DSSPContext) {
-    const { proteinResidues, hbonds, flags, hierarchy } = ctx
-    const { chains, residueAtomSegments, chainAtomSegments } = hierarchy
-    const { label_asym_id } = chains
-
-    const turnFlag = [DSSPType.Flag.T3S, DSSPType.Flag.T4S, DSSPType.Flag.T5S, DSSPType.Flag.T3, DSSPType.Flag.T4, DSSPType.Flag.T5]
-
-    for (let idx = 0; idx < 3; idx++) {
-        for (let i = 0, il = proteinResidues.length - 1; i < il; ++i) {
-            const rI = proteinResidues[i]
-            const cI = chainAtomSegments.index[residueAtomSegments.offsets[rI]]
-
-            // TODO should take sequence gaps into account
-            const rN = proteinResidues[i + idx + 3]
-            const cN = chainAtomSegments.index[residueAtomSegments.offsets[rN]]
-            // check if on same chain
-            if (!label_asym_id.areValuesEqual(cI, cN)) continue
-
-            // check if hbond exists
-            if (hbonds.getDirectedEdgeIndex(i, i + idx + 3) !== -1) {
-                flags[i] |= turnFlag[idx + 3] | turnFlag[idx]
-                if (ctx.params.oldDefinition) {
-                    for (let k = 1; k < idx + 3; ++k) {
-                        flags[i + k] |= turnFlag[idx + 3] | DSSPType.Flag.T
-                    }
-                } else {
-                    for (let k = 0; k <= idx + 3; ++k) {
-                        flags[i + k] |= turnFlag[idx + 3] | DSSPType.Flag.T
-                    }
-                }
-            }
-        }
-    }
-}
-
-/**
- * Two nonoverlapping stretches of three residues each, i - 1, i, i + 1 and j - 1, j, j + 1,
- * form either a parallel or antiparallel bridge, depending on which of
- * two basic patterns is matched. We assign a bridge between residues i and j
- * if there are two H bonds characteristic of P-structure; in particular,
- *
- * Parallel Bridge(i, j) =:
- *      [Hbond(i - 1, j) and Hbond(j, i + 1)] or
- *      [Hbond(j - 1, i) and Hbond(i, j + 1)]
- *
- * Antiparallel Bridge(i, j) =:
- *      [Hbond(i, j) and Hbond(j, i)] or
- *      [Hbond(i - 1, j + 1) and Hbond(j - 1, i + l)]
- *
- * Type: B
- */
-function assignBridges(ctx: DSSPContext) {
-    const { proteinResidues, hbonds, flags, bridges } = ctx
-
-    const { offset, b } = hbonds
-    let i: number, j: number
-
-    for (let k = 0, kl = proteinResidues.length; k < kl; ++k) {
-        for (let t = offset[k], _t = offset[k + 1]; t < _t; t++) {
-            const l = b[t]
-            if (k > l) continue
-
-            // Parallel Bridge(i, j) =: [Hbond(i - 1, j) and Hbond(j, i + 1)]
-            i = k + 1 // k is i - 1
-            j = l
-            if (i !== j && hbonds.getDirectedEdgeIndex(j, i + 1) !== -1) {
-                flags[i] |= DSSPType.Flag.B
-                flags[j] |= DSSPType.Flag.B
-                // TODO move to constructor, actually omit object all together
-                bridges[bridges.length] = new Bridge(i, j, BridgeType.PARALLEL)
-            }
-
-            // Parallel Bridge(i, j) =: [Hbond(j - 1, i) and Hbond(i, j + 1)]
-            i = k
-            j = l - 1 // l is j + 1
-            if (i !== j && hbonds.getDirectedEdgeIndex(j - 1, i) !== -1) {
-                flags[i] |= DSSPType.Flag.B
-                flags[j] |= DSSPType.Flag.B
-                bridges[bridges.length] = new Bridge(j, i, BridgeType.PARALLEL)
-            }
-
-            // Antiparallel Bridge(i, j) =: [Hbond(i, j) and Hbond(j, i)]
-            i = k
-            j = l
-            if (i !== j && hbonds.getDirectedEdgeIndex(j, i) !== -1) {
-                flags[i] |= DSSPType.Flag.B
-                flags[j] |= DSSPType.Flag.B
-                bridges[bridges.length] = new Bridge(j, i, BridgeType.ANTI_PARALLEL)
-            }
-
-            // Antiparallel Bridge(i, j) =: [Hbond(i - 1, j + 1) and Hbond(j - 1, i + l)]
-            i = k + 1
-            j = l - 1
-            if (i !== j && hbonds.getDirectedEdgeIndex(j - 1, i + 1) !== -1) {
-                flags[i] |= DSSPType.Flag.B
-                flags[j] |= DSSPType.Flag.B
-                bridges[bridges.length] = new Bridge(j, i, BridgeType.ANTI_PARALLEL)
-            }
-        }
-    }
-
-    bridges.sort((a, b) => a.partner1 > b.partner1 ? 1 : a.partner1 < b.partner1 ? -1 : 0)
-}
-
-/**
- * A minimal helix is defined by two consecutive n-turns.
- * For example, a 4-helix, of minimal length 4 from residues i to i + 3,
- * requires 4-turns at residues i - 1 and i,
- *
- *      3-helix(i,i + 2)=: [3-turn(i - 1) and 3-turn(i)]
- *      4-helix(i,i + 3)=: [4-turn(i - 1) and 4-turn(i)]
- *      5-helix(i,i + 4)=: [5-turn(i - 1) and 5-turn(i)]
- *
- * Type: G (n=3), H (n=4), I (n=5)
- */
-function assignHelices(ctx: DSSPContext) {
-    const { proteinResidues, flags } = ctx
-
-    const turnFlag = [DSSPType.Flag.T3S, DSSPType.Flag.T4S, DSSPType.Flag.T5S, DSSPType.Flag.T3, DSSPType.Flag.T4, DSSPType.Flag.T5]
-    const helixFlag = [0, 0, 0, DSSPType.Flag.G, DSSPType.Flag.H, DSSPType.Flag.I]
-
-    const helixCheckOrder = ctx.params.oldOrdering ? [4, 3, 5] : [3, 4, 5]
-    for (let ni = 0; ni < helixCheckOrder.length; ni++) {
-        const n = helixCheckOrder[ni]
-
-        for (let i = 1, il = proteinResidues.length - n; i < il; i++) {
-            const fI = DSSPType.create(flags[i])
-            const fI1 = DSSPType.create(flags[i - 1])
-            const fI2 = DSSPType.create(flags[i + 1])
-
-            // TODO rework to elegant solution which will not break instantly
-            if (ctx.params.oldOrdering) {
-                if ((n === 3 && (DSSPType.is(fI, DSSPType.Flag.H) || DSSPType.is(fI2, DSSPType.Flag.H)) || // for 3-10 yield to alpha helix
-                    (n === 5 && ((DSSPType.is(fI, DSSPType.Flag.H) || DSSPType.is(fI, DSSPType.Flag.G)) || (DSSPType.is(fI2, DSSPType.Flag.H) || DSSPType.is(fI2, DSSPType.Flag.G)))))) { // for pi yield to all other helices
-                    continue
-                }
-            } else {
-                if ((n === 4 && (DSSPType.is(fI, DSSPType.Flag.G) || DSSPType.is(fI2, DSSPType.Flag.G)) || // for alpha helix yield to 3-10
-                    (n === 5 && ((DSSPType.is(fI, DSSPType.Flag.H) || DSSPType.is(fI, DSSPType.Flag.G)) || (DSSPType.is(fI2, DSSPType.Flag.H) || DSSPType.is(fI2, DSSPType.Flag.G)))))) { // for pi yield to all other helices
-                    continue
-                }
-            }
-
-            if (DSSPType.is(fI, turnFlag[n]) && DSSPType.is(fI, turnFlag[n - 3]) && // check fI for turn start of proper type
-                DSSPType.is(fI1, turnFlag[n]) && DSSPType.is(fI1, turnFlag[n - 3])) { // check fI1 accordingly
-                if (ctx.params.oldDefinition) {
-                    for (let k = 0; k < n; k++) {
-                        flags[i + k] |= helixFlag[n]
-                    }
-                } else {
-                    for (let k = -1; k <= n; k++) {
-                        flags[i + k] |= helixFlag[n]
-                    }
-                }
-            }
-        }
-    }
-}
-
-/**
- * ladder=: set of one or more consecutive bridges of identical type
- *
- * Type: E
- */
-function assignLadders(ctx: DSSPContext) {
-    const { bridges, ladders } = ctx
-
-    // create ladders
-    for (let bridgeIndex = 0; bridgeIndex < bridges.length; bridgeIndex++) {
-        const bridge = bridges[bridgeIndex]
-        let found = false
-        for (let ladderIndex = 0; ladderIndex < ladders.length; ladderIndex++) {
-            const ladder = ladders[ladderIndex]
-            if (shouldExtendLadder(ladder, bridge)) {
-                found = true
-                ladder.firstEnd++
-                if (bridge.type === BridgeType.PARALLEL) {
-                    ladder.secondEnd++
-                } else {
-                    ladder.secondStart--
-                }
-            }
-        }
-
-        // no suitable assignment: create new ladder with single bridge as content
-        if (!found) {
-            ladders[ladders.length] = {
-                previousLadder: 0,
-                nextLadder: 0,
-                firstStart: bridge.partner1,
-                firstEnd: bridge.partner1,
-                secondStart: bridge.partner2,
-                secondEnd: bridge.partner2,
-                type: bridge.type
-            }
-        }
-    }
-
-    // connect ladders
-    for (let ladderIndex1 = 0; ladderIndex1 < ladders.length; ladderIndex1++) {
-        const ladder1 = ladders[ladderIndex1]
-        for (let ladderIndex2 = ladderIndex1; ladderIndex2 < ladders.length; ladderIndex2++) {
-            const ladder2 = ladders[ladderIndex2]
-            if (resemblesBulge(ladder1, ladder2)) {
-                ladder1.nextLadder = ladderIndex2
-                ladder2.previousLadder = ladderIndex1
-            }
-        }
-    }
-}
-
-/**
- * For beta structures, we define: a bulge-linked ladder consists of two ladders or bridges of the same type
- * connected by at most one extra residue of one strand and at most four extra residues  on the other strand,
- * all residues in bulge-linked ladders are marked E, including any extra residues.
- */
-function resemblesBulge(ladder1: Ladder, ladder2: Ladder) {
-    if (!(ladder1.type === ladder2.type && ladder2.firstStart - ladder1.firstEnd < 6 &&
-        ladder1.firstStart < ladder2.firstStart && ladder2.nextLadder === 0)) return false
-
-    if (ladder1.type === BridgeType.PARALLEL) {
-        return bulgeCriterion2(ladder1, ladder2)
-    } else {
-        return bulgeCriterion2(ladder2, ladder1)
-    }
-}
-
-function bulgeCriterion2(ladder1: Ladder, ladder2: Ladder) {
-    return ladder2.secondStart - ladder1.secondEnd > 0 && ((ladder2.secondStart - ladder1.secondEnd < 6 &&
-        ladder2.firstStart - ladder1.firstEnd < 3) || ladder2.secondStart - ladder1.secondEnd < 3)
-}
-
-function shouldExtendLadder(ladder: Ladder, bridge: Bridge): boolean {
-    // in order to extend ladders, same type must be present
-    if (bridge.type !== ladder.type) return false
-
-    // only extend if residue 1 is sequence neighbor with regard to ladder
-    if (bridge.partner1 !== ladder.firstEnd + 1) return false
-
-    if (bridge.type === BridgeType.PARALLEL) {
-        if (bridge.partner2 === ladder.secondEnd + 1) {
-            return true
-        }
-    } else {
-        if (bridge.partner2 === ladder.secondStart - 1) {
-            return true
-        }
-    }
-
-    return false
-}
-
-function isHelixType(f: DSSPType) {
-    return DSSPType.is(f, DSSPType.Flag.G) || DSSPType.is(f, DSSPType.Flag.H) || DSSPType.is(f, DSSPType.Flag.I)
-}
-
-/**
- * sheet=: set of one or more ladders connected by shared residues
- *
- * Type: E
- */
-function assignSheets(ctx: DSSPContext) {
-    const { ladders, flags } = ctx
-    for (let ladderIndex = 0; ladderIndex < ladders.length; ladderIndex++) {
-        const ladder = ladders[ladderIndex]
-        for (let lcount = ladder.firstStart; lcount <= ladder.firstEnd; lcount++) {
-            const diff = ladder.firstStart - lcount
-            const l2count = ladder.secondStart - diff
-
-            if (ladder.firstStart !== ladder.firstEnd) {
-                flags[lcount] |= DSSPType.Flag.E
-                flags[l2count] |= DSSPType.Flag.E
-            } else {
-                if (!isHelixType(flags[lcount]) && DSSPType.is(flags[lcount], DSSPType.Flag.E)) {
-                    flags[lcount] |= DSSPType.Flag.B
-                }
-                if (!isHelixType(flags[l2count]) && DSSPType.is(flags[l2count], DSSPType.Flag.E)) {
-                    flags[l2count] |= DSSPType.Flag.B
-                }
-            }
-        }
-
-        if (ladder.nextLadder === 0) continue
-
-        const conladder = ladders[ladder.nextLadder]
-        for (let lcount = ladder.firstStart; lcount <= conladder.firstEnd; lcount++) {
-            flags[lcount] |= DSSPType.Flag.E
-        }
-        if (ladder.type === BridgeType.PARALLEL) {
-            for (let lcount = ladder.secondStart; lcount <= conladder.secondEnd; lcount++) {
-                flags[lcount] |= DSSPType.Flag.E
-            }
-        } else {
-            for (let lcount = conladder.secondEnd; lcount <= ladder.secondStart; lcount++) {
-                flags[lcount] |= DSSPType.Flag.E
-            }
-        }
-    }
 }
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/backbone-hbonds.ts b/src/mol-model-props/computed/secondary-structure/dssp/backbone-hbonds.ts
new file mode 100644
index 000000000..159d1f07e
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/backbone-hbonds.ts
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { AtomicHierarchy, AtomicConformation } from 'mol-model/structure/model/properties/atomic';
+import { SortedArray } from 'mol-data/int';
+import { IntAdjacencyGraph } from 'mol-math/graph';
+import { ResidueIndex } from 'mol-model/structure';
+import { GridLookup3D } from 'mol-math/geometry';
+import { Vec3 } from 'mol-math/linear-algebra';
+import { DsspHbonds, BackboneAtomIndices } from './common';
+
+/** max distance between two C-alpha atoms to check for hbond */
+const caMaxDist = 9.0;
+
+/**
+ * Constant for electrostatic energy in kcal/mol
+ *      f  *  q1 *   q2
+ * Q = -332 * 0.42 * 0.20
+ *
+ * f is the dimensional factor
+ *
+ * q1 and q2 are partial charges which are placed on the C,O
+ * (+q1,-q1) and N,H (-q2,+q2)
+ */
+const Q = -27.888
+
+/** cutoff for hbonds in kcal/mol, must be lower to be consider as an hbond */
+const hbondEnergyCutoff = -0.5
+/** prevent extremely low hbond energies */
+const hbondEnergyMinimal = -9.9
+
+/**
+ * E = Q * (1/r(ON) + l/r(CH) - l/r(OH) - l/r(CN))
+ */
+function calcHbondEnergy(oPos: Vec3, cPos: Vec3, nPos: Vec3, hPos: Vec3) {
+    const distOH = Vec3.distance(oPos, hPos)
+    const distCH = Vec3.distance(cPos, hPos)
+    const distCN = Vec3.distance(cPos, nPos)
+    const distON = Vec3.distance(oPos, nPos)
+
+    const e1 = Q / distOH - Q / distCH
+    const e2 = Q / distCN - Q / distON
+    const e = e1 + e2
+
+    // cap lowest possible energy
+    if (e < hbondEnergyMinimal)
+        return hbondEnergyMinimal
+
+    return e
+}
+
+export function calcBackboneHbonds(hierarchy: AtomicHierarchy, conformation: AtomicConformation, proteinResidues: SortedArray<ResidueIndex>, backboneIndices: BackboneAtomIndices, lookup3d: GridLookup3D): DsspHbonds {
+    const { cIndices, hIndices, nIndices, oIndices } = backboneIndices
+    const { index } = hierarchy
+    const { x, y, z } = conformation
+    const { traceElementIndex } = hierarchy.derived.residue
+
+    const residueCount = proteinResidues.length
+    const position = (i: number, v: Vec3) => Vec3.set(v, x[i], y[i], z[i])
+
+    const oAtomResidues: number[] = [];
+    const nAtomResidues: number[] = [];
+    const energies: number[] = [];
+
+    const oPos = Vec3()
+    const cPos = Vec3()
+    const caPos = Vec3()
+    const nPos = Vec3()
+    const hPos = Vec3()
+
+    const cPosPrev = Vec3()
+    const oPosPrev = Vec3()
+
+    for (let i = 0, il = proteinResidues.length; i < il; ++i) {
+        const oPI = i
+        const oRI = proteinResidues[i]
+
+        const oAtom = oIndices[oPI]
+        const cAtom = cIndices[oPI]
+        const caAtom = traceElementIndex[oRI]
+
+        // continue if residue is missing O or C atom
+        if (oAtom === -1 || cAtom === -1) continue
+
+        // ignore C-terminal residue as acceptor
+        if (index.findAtomOnResidue(oRI, 'OXT') !== -1) continue
+
+        position(oAtom, oPos)
+        position(cAtom, cPos)
+        position(caAtom, caPos)
+
+        const { indices, count } = lookup3d.find(caPos[0], caPos[1], caPos[2], caMaxDist)
+
+        for (let j = 0; j < count; ++j) {
+            const nPI = indices[j]
+
+            // ignore bonds within a residue or to prev or next residue, TODO take chain border into account
+            if (nPI === oPI || nPI - 1 === oPI || nPI + 1 === oPI) continue
+
+            const nAtom = nIndices[nPI]
+            if (nAtom === -1) continue
+
+            position(nAtom, nPos)
+
+            const hAtom = hIndices[nPI]
+            if (hAtom === -1) {
+                // approximate calculation of H position, TODO factor out
+                if (nPI === 0) continue
+                const nPIprev = nPI - 1
+
+                const oAtomPrev = oIndices[nPIprev]
+                const cAtomPrev = cIndices[nPIprev]
+                if (oAtomPrev === -1 || cAtomPrev === -1) continue
+
+                position(oAtomPrev, oPosPrev)
+                position(cAtomPrev, cPosPrev)
+
+                Vec3.sub(hPos, cPosPrev, oPosPrev)
+                const dist = Vec3.distance(oPosPrev, cPosPrev)
+                Vec3.scaleAndAdd(hPos, nPos, hPos, 1 / dist)
+            } else {
+                position(hAtom, hPos)
+            }
+
+            const e = calcHbondEnergy(oPos, cPos, nPos, hPos)
+            if (e > hbondEnergyCutoff) continue
+
+            oAtomResidues[oAtomResidues.length] = oPI
+            nAtomResidues[nAtomResidues.length] = nPI
+            energies[energies.length] = e
+        }
+    }
+
+    return buildHbondGraph(residueCount, oAtomResidues, nAtomResidues, energies);
+}
+
+function buildHbondGraph(residueCount: number, oAtomResidues: number[], nAtomResidues: number[], energies: number[]) {
+    const builder = new IntAdjacencyGraph.DirectedEdgeBuilder(residueCount, oAtomResidues, nAtomResidues);
+    const _energies = new Float32Array(builder.slotCount);
+
+    for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
+        builder.addNextEdge();
+        builder.assignProperty(_energies, energies[i]);
+    }
+
+    return builder.createGraph({ energies });
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/backbone-indices.ts b/src/mol-model-props/computed/secondary-structure/dssp/backbone-indices.ts
new file mode 100644
index 000000000..af1ba5c3d
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/backbone-indices.ts
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { AtomicHierarchy } from 'mol-model/structure/model/properties/atomic';
+import { SortedArray } from 'mol-data/int';
+import { ResidueIndex, ElementIndex } from 'mol-model/structure';
+import { BackboneAtomIndices } from './common';
+
+export function calcBackboneAtomIndices(hierarchy: AtomicHierarchy, proteinResidues: SortedArray<ResidueIndex>): BackboneAtomIndices {
+    const residueCount = proteinResidues.length
+    const { index } = hierarchy
+
+    const c = new Int32Array(residueCount)
+    const h = new Int32Array(residueCount)
+    const o = new Int32Array(residueCount)
+    const n = new Int32Array(residueCount)
+
+    for (let i = 0, il = residueCount; i < il; ++i) {
+        const rI = proteinResidues[i]
+        c[i] = index.findAtomOnResidue(rI, 'C')
+        h[i] = index.findAtomOnResidue(rI, 'H')
+        o[i] = index.findAtomOnResidue(rI, 'O')
+        n[i] = index.findAtomOnResidue(rI, 'N')
+    }
+
+    return {
+        cIndices: c as unknown as ArrayLike<ElementIndex | -1>,
+        hIndices: h as unknown as ArrayLike<ElementIndex | -1>,
+        oIndices: o as unknown as ArrayLike<ElementIndex | -1>,
+        nIndices: n as unknown as ArrayLike<ElementIndex | -1>,
+    }
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/bends.ts b/src/mol-model-props/computed/secondary-structure/dssp/bends.ts
new file mode 100644
index 000000000..c252d2f59
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/bends.ts
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { Vec3 } from 'mol-math/linear-algebra';
+import { radToDeg } from 'mol-math/misc';
+import { DSSPContext, DSSPType } from './common';
+
+/**
+ * Bend(i) =: [angle ((CW - Ca(i - 2)),(C"(i + 2) - C"(i))) > 70"]
+ *
+ * Type: S
+ */
+export function assignBends(ctx: DSSPContext) {
+    const flags = ctx.flags
+    const { x, y, z } = ctx.conformation
+    const { traceElementIndex } = ctx.hierarchy.derived.residue
+
+    const proteinResidues = ctx.proteinResidues
+    const residueCount = proteinResidues.length
+
+    const position = (i: number, v: Vec3) => Vec3.set(v, x[i], y[i], z[i])
+
+    const caPosPrev2 = Vec3()
+    const caPos = Vec3()
+    const caPosNext2 = Vec3()
+
+    const nIndices = ctx.backboneIndices.nIndices
+    const cPos = Vec3()
+    const nPosNext = Vec3()
+
+    const caMinus2 = Vec3()
+    const caPlus2 = Vec3()
+
+    f1: for (let i = 2; i < residueCount - 2; i++) {
+        // check for peptide bond
+        for (let k = 0; k < 4; k++) {
+            let index = i + k - 2
+            position(traceElementIndex[index], cPos)
+            position(nIndices[index + 1], nPosNext)
+            if (Vec3.squaredDistance(cPos, nPosNext) > 6.25 /* max squared peptide bond distance allowed */) {
+                continue f1
+            }
+        }
+
+        const oRIprev2 = proteinResidues[i - 2]
+        const oRI = proteinResidues[i]
+        const oRInext2 = proteinResidues[i + 2]
+
+        const caAtomPrev2 = traceElementIndex[oRIprev2]
+        const caAtom = traceElementIndex[oRI]
+        const caAtomNext2 = traceElementIndex[oRInext2]
+
+        position(caAtomPrev2, caPosPrev2)
+        position(caAtom, caPos)
+        position(caAtomNext2, caPosNext2)
+
+        Vec3.sub(caMinus2, caPosPrev2, caPos)
+        Vec3.sub(caPlus2, caPos, caPosNext2)
+
+        const angle = radToDeg(Vec3.angle(caMinus2, caPlus2))
+        if (angle && angle > 70.00) {
+            flags[i] |= DSSPType.Flag.S
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/bridges.ts b/src/mol-model-props/computed/secondary-structure/dssp/bridges.ts
new file mode 100644
index 000000000..42d60d006
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/bridges.ts
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { DSSPContext, DSSPType, BridgeType, Bridge } from './common';
+
+/**
+ * Two nonoverlapping stretches of three residues each, i - 1, i, i + 1 and j - 1, j, j + 1,
+ * form either a parallel or antiparallel bridge, depending on which of
+ * two basic patterns is matched. We assign a bridge between residues i and j
+ * if there are two H bonds characteristic of P-structure; in particular,
+ *
+ * Parallel Bridge(i, j) =:
+ *      [Hbond(i - 1, j) and Hbond(j, i + 1)] or
+ *      [Hbond(j - 1, i) and Hbond(i, j + 1)]
+ *
+ * Antiparallel Bridge(i, j) =:
+ *      [Hbond(i, j) and Hbond(j, i)] or
+ *      [Hbond(i - 1, j + 1) and Hbond(j - 1, i + l)]
+ *
+ * Type: B
+ */
+export function assignBridges(ctx: DSSPContext) {
+    const { proteinResidues, hbonds, flags, bridges } = ctx
+
+    const { offset, b } = hbonds
+    let i: number, j: number
+
+    for (let k = 0, kl = proteinResidues.length; k < kl; ++k) {
+        for (let t = offset[k], _t = offset[k + 1]; t < _t; t++) {
+            const l = b[t]
+            if (k > l) continue
+
+            // Parallel Bridge(i, j) =: [Hbond(i - 1, j) and Hbond(j, i + 1)]
+            i = k + 1 // k is i - 1
+            j = l
+            if (i !== j && hbonds.getDirectedEdgeIndex(j, i + 1) !== -1) {
+                flags[i] |= DSSPType.Flag.B
+                flags[j] |= DSSPType.Flag.B
+                // TODO move to constructor, actually omit object all together
+                bridges[bridges.length] = new Bridge(i, j, BridgeType.PARALLEL)
+            }
+
+            // Parallel Bridge(i, j) =: [Hbond(j - 1, i) and Hbond(i, j + 1)]
+            i = k
+            j = l - 1 // l is j + 1
+            if (i !== j && hbonds.getDirectedEdgeIndex(j - 1, i) !== -1) {
+                flags[i] |= DSSPType.Flag.B
+                flags[j] |= DSSPType.Flag.B
+                bridges[bridges.length] = new Bridge(j, i, BridgeType.PARALLEL)
+            }
+
+            // Antiparallel Bridge(i, j) =: [Hbond(i, j) and Hbond(j, i)]
+            i = k
+            j = l
+            if (i !== j && hbonds.getDirectedEdgeIndex(j, i) !== -1) {
+                flags[i] |= DSSPType.Flag.B
+                flags[j] |= DSSPType.Flag.B
+                bridges[bridges.length] = new Bridge(j, i, BridgeType.ANTI_PARALLEL)
+            }
+
+            // Antiparallel Bridge(i, j) =: [Hbond(i - 1, j + 1) and Hbond(j - 1, i + l)]
+            i = k + 1
+            j = l - 1
+            if (i !== j && hbonds.getDirectedEdgeIndex(j - 1, i + 1) !== -1) {
+                flags[i] |= DSSPType.Flag.B
+                flags[j] |= DSSPType.Flag.B
+                bridges[bridges.length] = new Bridge(j, i, BridgeType.ANTI_PARALLEL)
+            }
+        }
+    }
+
+    bridges.sort((a, b) => a.partner1 > b.partner1 ? 1 : a.partner1 < b.partner1 ? -1 : 0)
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/common.ts b/src/mol-model-props/computed/secondary-structure/dssp/common.ts
new file mode 100644
index 000000000..bfbf85193
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/common.ts
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { BitFlags } from 'mol-util';
+import { SecondaryStructureType } from 'mol-model/structure/model/types';
+import { AtomicHierarchy, AtomicConformation } from 'mol-model/structure/model/properties/atomic';
+import { SortedArray } from 'mol-data/int';
+import { ResidueIndex, ElementIndex } from 'mol-model/structure';
+import { IntAdjacencyGraph } from 'mol-math/graph';
+
+export interface DSSPContext {
+    params: {
+        oldDefinition: boolean
+        oldOrdering: boolean
+    },
+    getResidueFlag: (f: DSSPType) => SecondaryStructureType,
+    getFlagName: (f: DSSPType) => String,
+
+    hierarchy: AtomicHierarchy
+    proteinResidues: SortedArray<ResidueIndex>
+    /** flags for each residue */
+    flags: Uint32Array
+    hbonds: DsspHbonds,
+
+    torsionAngles: { phi: Float32Array, psi: Float32Array },
+    backboneIndices: BackboneAtomIndices,
+    conformation: AtomicConformation,
+    ladders: Ladder[],
+    bridges: Bridge[]
+}
+
+export { DSSPType }
+type DSSPType = BitFlags<DSSPType.Flag>
+namespace DSSPType {
+    export const is: (t: DSSPType, f: Flag) => boolean = BitFlags.has
+    export const create: (f: Flag) => DSSPType = BitFlags.create
+    export const enum Flag {
+        _ = 0x0,
+        H = 0x1,
+        B = 0x2,
+        E = 0x4,
+        G = 0x8,
+        I = 0x10,
+        S = 0x20,
+        T = 0x40,
+        T3 = 0x80,
+        T4 = 0x100,
+        T5 = 0x200,
+        T3S = 0x400, // marks 3-turn start
+        T4S = 0x800,
+        T5S = 0x1000
+    }
+}
+
+export interface BackboneAtomIndices {
+    cIndices: ArrayLike<ElementIndex | -1>
+    hIndices: ArrayLike<ElementIndex | -1>
+    oIndices: ArrayLike<ElementIndex | -1>
+    nIndices: ArrayLike<ElementIndex | -1>
+}
+
+export type DsspHbonds = IntAdjacencyGraph<{ readonly energies: ArrayLike<number> }>
+
+export interface Ladder {
+    previousLadder: number,
+    nextLadder: number,
+    firstStart: number,
+    secondStart: number,
+    secondEnd: number,
+    firstEnd: number,
+    type: BridgeType
+}
+
+export const enum BridgeType {
+    PARALLEL = 0x0,
+    ANTI_PARALLEL = 0x1
+}
+
+export class Bridge {
+    partner1: number;
+    partner2: number;
+    type: BridgeType;
+
+    constructor(p1: number, p2: number, type: BridgeType) {
+        this.partner1 = Math.min(p1, p2)
+        this.partner2 = Math.max(p1, p2)
+        this.type = type
+    }
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/dihedral-angles.ts b/src/mol-model-props/computed/secondary-structure/dssp/dihedral-angles.ts
new file mode 100644
index 000000000..67d1ad488
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/dihedral-angles.ts
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { AtomicHierarchy, AtomicConformation } from 'mol-model/structure/model/properties/atomic';
+import { BackboneAtomIndices } from './common';
+import { ResidueIndex } from 'mol-model/structure';
+import { SortedArray } from 'mol-data/int';
+import { Vec3 } from 'mol-math/linear-algebra';
+
+export function calculateDihedralAngles(hierarchy: AtomicHierarchy, conformation: AtomicConformation, proteinResidues: SortedArray<ResidueIndex>, backboneIndices: BackboneAtomIndices): { phi: Float32Array, psi: Float32Array } {
+    const { cIndices, nIndices } = backboneIndices
+    const { index } = hierarchy
+    const { x, y, z } = conformation
+    const { traceElementIndex } = hierarchy.derived.residue
+
+    const residueCount = proteinResidues.length
+    const position = (i: number, v: Vec3) => i === -1 ? Vec3.setNaN(v) : Vec3.set(v, x[i], y[i], z[i])
+
+    let cPosPrev = Vec3(), caPosPrev = Vec3(), nPosPrev = Vec3()
+    let cPos = Vec3(), caPos = Vec3(), nPos = Vec3()
+    let cPosNext = Vec3(), caPosNext = Vec3(), nPosNext = Vec3()
+
+    if (residueCount === 0) return { phi: new Float32Array(0), psi: new Float32Array(0) }
+
+    const phi: Float32Array = new Float32Array(residueCount - 1)
+    const psi: Float32Array = new Float32Array(residueCount - 1)
+
+    position(-1, cPosPrev)
+    position(-1, caPosPrev)
+    position(-1, nPosPrev)
+
+    position(cIndices[0], cPos)
+    position(traceElementIndex[proteinResidues[0]], caPos)
+    position(nIndices[0], nPos)
+
+    position(cIndices[1], cPosNext)
+    position(traceElementIndex[proteinResidues[1]], caPosNext)
+    position(nIndices[1], nPosNext)
+
+    for (let i = 0; i < residueCount - 1; ++i) {
+        // ignore C-terminal residue as acceptor
+        if (index.findAtomOnResidue(proteinResidues[i], 'OXT') !== -1) continue
+
+        // returns NaN for missing atoms
+        phi[i] = Vec3.dihedralAngle(cPosPrev, nPos, caPos, cPos)
+        psi[i] = Vec3.dihedralAngle(nPos, caPos, cPos, nPosNext)
+
+        cPosPrev = cPos, caPosPrev = caPos, nPosPrev = nPos
+        cPos = cPosNext, caPos = caPosNext, nPos = nPosNext
+
+        position(cIndices[i + 1], cPosNext)
+        position(traceElementIndex[proteinResidues[i + 1]], caPosNext)
+        position(nIndices[i + 1], nPosNext)
+    }
+
+    return { phi, psi };
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/helices.ts b/src/mol-model-props/computed/secondary-structure/dssp/helices.ts
new file mode 100644
index 000000000..3e81dd4dc
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/helices.ts
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { DSSPContext, DSSPType } from './common';
+
+/**
+ * A minimal helix is defined by two consecutive n-turns.
+ * For example, a 4-helix, of minimal length 4 from residues i to i + 3,
+ * requires 4-turns at residues i - 1 and i,
+ *
+ *      3-helix(i,i + 2)=: [3-turn(i - 1) and 3-turn(i)]
+ *      4-helix(i,i + 3)=: [4-turn(i - 1) and 4-turn(i)]
+ *      5-helix(i,i + 4)=: [5-turn(i - 1) and 5-turn(i)]
+ *
+ * Type: G (n=3), H (n=4), I (n=5)
+ */
+export function assignHelices(ctx: DSSPContext) {
+    const { proteinResidues, flags } = ctx
+
+    const turnFlag = [DSSPType.Flag.T3S, DSSPType.Flag.T4S, DSSPType.Flag.T5S, DSSPType.Flag.T3, DSSPType.Flag.T4, DSSPType.Flag.T5]
+    const helixFlag = [0, 0, 0, DSSPType.Flag.G, DSSPType.Flag.H, DSSPType.Flag.I]
+
+    const helixCheckOrder = ctx.params.oldOrdering ? [4, 3, 5] : [3, 4, 5]
+    for (let ni = 0; ni < helixCheckOrder.length; ni++) {
+        const n = helixCheckOrder[ni]
+
+        for (let i = 1, il = proteinResidues.length - n; i < il; i++) {
+            const fI = DSSPType.create(flags[i])
+            const fI1 = DSSPType.create(flags[i - 1])
+            const fI2 = DSSPType.create(flags[i + 1])
+
+            // TODO rework to elegant solution which will not break instantly
+            if (ctx.params.oldOrdering) {
+                if ((n === 3 && (DSSPType.is(fI, DSSPType.Flag.H) || DSSPType.is(fI2, DSSPType.Flag.H)) || // for 3-10 yield to alpha helix
+                    (n === 5 && ((DSSPType.is(fI, DSSPType.Flag.H) || DSSPType.is(fI, DSSPType.Flag.G)) || (DSSPType.is(fI2, DSSPType.Flag.H) || DSSPType.is(fI2, DSSPType.Flag.G)))))) { // for pi yield to all other helices
+                    continue
+                }
+            } else {
+                if ((n === 4 && (DSSPType.is(fI, DSSPType.Flag.G) || DSSPType.is(fI2, DSSPType.Flag.G)) || // for alpha helix yield to 3-10
+                    (n === 5 && ((DSSPType.is(fI, DSSPType.Flag.H) || DSSPType.is(fI, DSSPType.Flag.G)) || (DSSPType.is(fI2, DSSPType.Flag.H) || DSSPType.is(fI2, DSSPType.Flag.G)))))) { // for pi yield to all other helices
+                    continue
+                }
+            }
+
+            if (DSSPType.is(fI, turnFlag[n]) && DSSPType.is(fI, turnFlag[n - 3]) && // check fI for turn start of proper type
+                DSSPType.is(fI1, turnFlag[n]) && DSSPType.is(fI1, turnFlag[n - 3])) { // check fI1 accordingly
+                if (ctx.params.oldDefinition) {
+                    for (let k = 0; k < n; k++) {
+                        flags[i + k] |= helixFlag[n]
+                    }
+                } else {
+                    for (let k = -1; k <= n; k++) {
+                        flags[i + k] |= helixFlag[n]
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/ladders.ts b/src/mol-model-props/computed/secondary-structure/dssp/ladders.ts
new file mode 100644
index 000000000..57a5c2ceb
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/ladders.ts
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { DSSPContext, Ladder, BridgeType, Bridge } from './common';
+
+function shouldExtendLadder(ladder: Ladder, bridge: Bridge): boolean {
+    // in order to extend ladders, same type must be present
+    if (bridge.type !== ladder.type) return false
+
+    // only extend if residue 1 is sequence neighbor with regard to ladder
+    if (bridge.partner1 !== ladder.firstEnd + 1) return false
+
+    if (bridge.type === BridgeType.PARALLEL) {
+        if (bridge.partner2 === ladder.secondEnd + 1) {
+            return true
+        }
+    } else {
+        if (bridge.partner2 === ladder.secondStart - 1) {
+            return true
+        }
+    }
+
+    return false
+}
+
+/**
+ * For beta structures, we define: a bulge-linked ladder consists of two ladders or bridges of the same type
+ * connected by at most one extra residue of one strand and at most four extra residues  on the other strand,
+ * all residues in bulge-linked ladders are marked E, including any extra residues.
+ */
+function resemblesBulge(ladder1: Ladder, ladder2: Ladder) {
+    if (!(ladder1.type === ladder2.type && ladder2.firstStart - ladder1.firstEnd < 6 &&
+        ladder1.firstStart < ladder2.firstStart && ladder2.nextLadder === 0)) return false
+
+    if (ladder1.type === BridgeType.PARALLEL) {
+        return bulgeCriterion2(ladder1, ladder2)
+    } else {
+        return bulgeCriterion2(ladder2, ladder1)
+    }
+}
+
+function bulgeCriterion2(ladder1: Ladder, ladder2: Ladder) {
+    return ladder2.secondStart - ladder1.secondEnd > 0 && ((ladder2.secondStart - ladder1.secondEnd < 6 &&
+        ladder2.firstStart - ladder1.firstEnd < 3) || ladder2.secondStart - ladder1.secondEnd < 3)
+}
+
+/**
+ * ladder=: set of one or more consecutive bridges of identical type
+ *
+ * Type: E
+ */
+export function assignLadders(ctx: DSSPContext) {
+    const { bridges, ladders } = ctx
+
+    // create ladders
+    for (let bridgeIndex = 0; bridgeIndex < bridges.length; bridgeIndex++) {
+        const bridge = bridges[bridgeIndex]
+        let found = false
+        for (let ladderIndex = 0; ladderIndex < ladders.length; ladderIndex++) {
+            const ladder = ladders[ladderIndex]
+            if (shouldExtendLadder(ladder, bridge)) {
+                found = true
+                ladder.firstEnd++
+                if (bridge.type === BridgeType.PARALLEL) {
+                    ladder.secondEnd++
+                } else {
+                    ladder.secondStart--
+                }
+            }
+        }
+
+        // no suitable assignment: create new ladder with single bridge as content
+        if (!found) {
+            ladders[ladders.length] = {
+                previousLadder: 0,
+                nextLadder: 0,
+                firstStart: bridge.partner1,
+                firstEnd: bridge.partner1,
+                secondStart: bridge.partner2,
+                secondEnd: bridge.partner2,
+                type: bridge.type
+            }
+        }
+    }
+
+    // connect ladders
+    for (let ladderIndex1 = 0; ladderIndex1 < ladders.length; ladderIndex1++) {
+        const ladder1 = ladders[ladderIndex1]
+        for (let ladderIndex2 = ladderIndex1; ladderIndex2 < ladders.length; ladderIndex2++) {
+            const ladder2 = ladders[ladderIndex2]
+            if (resemblesBulge(ladder1, ladder2)) {
+                ladder1.nextLadder = ladderIndex2
+                ladder2.previousLadder = ladderIndex1
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/sheets.ts b/src/mol-model-props/computed/secondary-structure/dssp/sheets.ts
new file mode 100644
index 000000000..ebd80b3d8
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/sheets.ts
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { DSSPContext, DSSPType, BridgeType } from './common';
+
+function isHelixType(f: DSSPType) {
+    return DSSPType.is(f, DSSPType.Flag.G) || DSSPType.is(f, DSSPType.Flag.H) || DSSPType.is(f, DSSPType.Flag.I)
+}
+
+/**
+ * sheet=: set of one or more ladders connected by shared residues
+ *
+ * Type: E
+ */
+export function assignSheets(ctx: DSSPContext) {
+    const { ladders, flags } = ctx
+    for (let ladderIndex = 0; ladderIndex < ladders.length; ladderIndex++) {
+        const ladder = ladders[ladderIndex]
+        for (let lcount = ladder.firstStart; lcount <= ladder.firstEnd; lcount++) {
+            const diff = ladder.firstStart - lcount
+            const l2count = ladder.secondStart - diff
+
+            if (ladder.firstStart !== ladder.firstEnd) {
+                flags[lcount] |= DSSPType.Flag.E
+                flags[l2count] |= DSSPType.Flag.E
+            } else {
+                if (!isHelixType(flags[lcount]) && DSSPType.is(flags[lcount], DSSPType.Flag.E)) {
+                    flags[lcount] |= DSSPType.Flag.B
+                }
+                if (!isHelixType(flags[l2count]) && DSSPType.is(flags[l2count], DSSPType.Flag.E)) {
+                    flags[l2count] |= DSSPType.Flag.B
+                }
+            }
+        }
+
+        if (ladder.nextLadder === 0) continue
+
+        const conladder = ladders[ladder.nextLadder]
+        for (let lcount = ladder.firstStart; lcount <= conladder.firstEnd; lcount++) {
+            flags[lcount] |= DSSPType.Flag.E
+        }
+        if (ladder.type === BridgeType.PARALLEL) {
+            for (let lcount = ladder.secondStart; lcount <= conladder.secondEnd; lcount++) {
+                flags[lcount] |= DSSPType.Flag.E
+            }
+        } else {
+            for (let lcount = conladder.secondEnd; lcount <= ladder.secondStart; lcount++) {
+                flags[lcount] |= DSSPType.Flag.E
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/trace-lookup.ts b/src/mol-model-props/computed/secondary-structure/dssp/trace-lookup.ts
new file mode 100644
index 000000000..352202378
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/trace-lookup.ts
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { AtomicHierarchy, AtomicConformation } from 'mol-model/structure/model/properties/atomic';
+import { MoleculeType } from 'mol-model/structure/model/types';
+import { GridLookup3D } from 'mol-math/geometry';
+import { SortedArray } from 'mol-data/int';
+import { ResidueIndex } from 'mol-model/structure';
+
+export function calcAtomicTraceLookup3D(hierarchy: AtomicHierarchy, conformation: AtomicConformation) {
+    const { x, y, z } = conformation;
+    const { moleculeType, traceElementIndex } = hierarchy.derived.residue
+    const indices: number[] = []
+    const _proteinResidues: number[] = []
+    for (let i = 0, il = moleculeType.length; i < il; ++i) {
+        if (moleculeType[i] === MoleculeType.protein) {
+            indices[indices.length] = traceElementIndex[i]
+            _proteinResidues[_proteinResidues.length] = i
+        }
+    }
+    const lookup3d = GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(indices) }, 4);
+    const proteinResidues = SortedArray.ofSortedArray<ResidueIndex>(_proteinResidues)
+    return { lookup3d, proteinResidues }
+}
\ No newline at end of file
diff --git a/src/mol-model-props/computed/secondary-structure/dssp/turns.ts b/src/mol-model-props/computed/secondary-structure/dssp/turns.ts
new file mode 100644
index 000000000..7a62a190d
--- /dev/null
+++ b/src/mol-model-props/computed/secondary-structure/dssp/turns.ts
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { DSSPContext, DSSPType } from './common';
+
+
+/**
+ * The basic turn pattern is a single H bond of type (i, i + n).
+ * We assign an n-turn at residue i if there is an H bond from CO(i) to NH(i + n),
+ * i.e., “n-turn(i)=: Hbond(i, i + n), n = 3, 4, 5.”
+ *
+ * Type: T
+ */
+export function assignTurns(ctx: DSSPContext) {
+    const { proteinResidues, hbonds, flags, hierarchy } = ctx
+    const { chains, residueAtomSegments, chainAtomSegments } = hierarchy
+    const { label_asym_id } = chains
+
+    const turnFlag = [DSSPType.Flag.T3S, DSSPType.Flag.T4S, DSSPType.Flag.T5S, DSSPType.Flag.T3, DSSPType.Flag.T4, DSSPType.Flag.T5]
+
+    for (let idx = 0; idx < 3; idx++) {
+        for (let i = 0, il = proteinResidues.length - 1; i < il; ++i) {
+            const rI = proteinResidues[i]
+            const cI = chainAtomSegments.index[residueAtomSegments.offsets[rI]]
+
+            // TODO should take sequence gaps into account
+            const rN = proteinResidues[i + idx + 3]
+            const cN = chainAtomSegments.index[residueAtomSegments.offsets[rN]]
+            // check if on same chain
+            if (!label_asym_id.areValuesEqual(cI, cN)) continue
+
+            // check if hbond exists
+            if (hbonds.getDirectedEdgeIndex(i, i + idx + 3) !== -1) {
+                flags[i] |= turnFlag[idx + 3] | turnFlag[idx]
+                if (ctx.params.oldDefinition) {
+                    for (let k = 1; k < idx + 3; ++k) {
+                        flags[i + k] |= turnFlag[idx + 3] | DSSPType.Flag.T
+                    }
+                } else {
+                    for (let k = 0; k <= idx + 3; ++k) {
+                        flags[i + k] |= turnFlag[idx + 3] | DSSPType.Flag.T
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
-- 
GitLab