Skip to content
Snippets Groups Projects
Commit 4c749096 authored by Alexander Rose's avatar Alexander Rose
Browse files

add gro format import support to mol-model

parent 9184dfaf
No related branches found
No related tags found
No related merge requests found
......@@ -46,6 +46,18 @@ namespace Table {
return { _rowCount, _columns, _schema: schema, ...(columns as any) };
}
export function ofUndefinedColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, rowCount: number): R {
const ret = Object.create(null);
const columns = Object.keys(schema);
ret._rowCount = rowCount;
ret._columns = columns;
ret._schema = schema;
for (const k of columns) {
ret[k] = Column.Undefined(rowCount, schema[k])
}
return ret;
}
export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Row<S>>): R {
const ret = Object.create(null);
const rowCount = rows.length;
......
......@@ -4,12 +4,15 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { File as GroFile } from 'mol-io/reader/gro/schema'
import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'
type Format =
| Format.gro
| Format.mmCIF
namespace Format {
export interface gro { kind: 'gro', data: GroFile }
export interface mmCIF { kind: 'mmCIF', data: mmCIF_Database }
}
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import UUID from 'mol-util/uuid'
import { Column, Table } from 'mol-data/db'
import { Interval, Segmentation } from 'mol-data/int'
import { Atoms } from 'mol-io/reader/gro/schema'
import Format from '../format'
import Model from '../model'
import * as Hierarchy from '../properties/hierarchy'
import Conformation from '../properties/conformation'
import findHierarchyKeys from '../utils/hierarchy-keys'
import { guessElement } from '../utils/guess-element'
import { ElementSymbol} from '../types'
import gro_Format = Format.gro
type HierarchyOffsets = { residues: ArrayLike<number>, chains: ArrayLike<number> }
function findHierarchyOffsets(atomsData: Atoms, bounds: Interval) {
const start = Interval.start(bounds), end = Interval.end(bounds);
const residues = [start], chains = [start];
const { residueName, residueNumber } = atomsData;
for (let i = start + 1; i < end; i++) {
const newResidue = !residueNumber.areValuesEqual(i - 1, i)
|| !residueName.areValuesEqual(i - 1, i);
console.log(residueName.value(i - 1), residueName.value(i), residueNumber.value(i - 1), residueNumber.value(i), newResidue)
if (newResidue) residues[residues.length] = i;
}
console.log(residues, residues.length)
return { residues, chains };
}
function guessElementSymbol (value: string) {
return ElementSymbol(guessElement(value));
}
function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): Hierarchy.Data {
console.log(atomsData.atomName)
const atoms = Table.ofColumns(Hierarchy.AtomsSchema, {
type_symbol: Column.ofArray({ array: Column.mapToArray(atomsData.atomName, guessElementSymbol), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) }),
label_atom_id: atomsData.atomName,
auth_atom_id: atomsData.atomName,
label_alt_id: Column.Undefined(atomsData.count, Column.Schema.str),
pdbx_formal_charge: Column.Undefined(atomsData.count, Column.Schema.int)
});
const residues = Table.view(Table.ofColumns(Hierarchy.ResiduesSchema, {
group_PDB: Column.Undefined(atomsData.count, Column.Schema.str),
label_comp_id: atomsData.residueName,
auth_comp_id: atomsData.residueName,
label_seq_id: atomsData.residueNumber,
auth_seq_id: atomsData.residueNumber,
pdbx_PDB_ins_code: Column.Undefined(atomsData.count, Column.Schema.str)
}), Hierarchy.ResiduesSchema, offsets.residues);
// Optimize the numeric columns
Table.columnToArray(residues, 'label_seq_id', Int32Array);
Table.columnToArray(residues, 'auth_seq_id', Int32Array);
// const chains = Table.ofColumns(Hierarchy.ChainsSchema, {
// label_asym_id: Column.ofConst('A', atomsData.count, Column.Schema.str),
// auth_asym_id: Column.ofConst('A', atomsData.count, Column.Schema.str),
// label_entity_id: Column.Undefined(atomsData.count, Column.Schema.str)
// });
const chains = Table.ofUndefinedColumns(Hierarchy.ChainsSchema, 0);
const entities = Table.ofUndefinedColumns(Hierarchy.EntitySchema, 0);
return { atoms, residues, chains, entities };
}
function getConformation(atoms: Atoms): Conformation {
return {
id: UUID.create(),
atomId: atoms.atomNumber,
occupancy: Column.Undefined(atoms.count, Column.Schema.int),
B_iso_or_equiv: Column.Undefined(atoms.count, Column.Schema.float),
x: Column.mapToArray(atoms.x, x => x * 10, Float32Array),
y: Column.mapToArray(atoms.y, y => y * 10, Float32Array),
z: Column.mapToArray(atoms.z, z => z * 10, Float32Array)
}
}
function isHierarchyDataEqual(a: Hierarchy.Hierarchy, b: Hierarchy.Data) {
// need to cast because of how TS handles type resolution for interfaces https://github.com/Microsoft/TypeScript/issues/15300
return Table.areEqual(a.residues as Table<Hierarchy.ResiduesSchema>, b.residues as Table<Hierarchy.ResiduesSchema>)
&& Table.areEqual(a.atoms as Table<Hierarchy.AtomsSchema>, b.atoms as Table<Hierarchy.AtomsSchema>)
}
function createModel(format: gro_Format, modelNum: number, previous?: Model): Model {
const structure = format.data.structures[modelNum];
const bounds = Interval.ofBounds(0, structure.atoms.count);
const hierarchyOffsets = findHierarchyOffsets(structure.atoms, bounds);
const hierarchyData = createHierarchyData(structure.atoms, hierarchyOffsets);
if (previous && isHierarchyDataEqual(previous.hierarchy, hierarchyData)) {
return {
...previous,
conformation: getConformation(structure.atoms)
};
}
const hierarchySegments: Hierarchy.Segments = {
residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds),
chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds),
}
const hierarchyKeys = findHierarchyKeys(hierarchyData, hierarchySegments);
return {
id: UUID.create(),
sourceData: format,
modelNum,
hierarchy: { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments },
conformation: getConformation(structure.atoms),
symmetry: { assemblies: [] },
atomCount: structure.atoms.count
};
}
function buildModels(format: gro_Format): ReadonlyArray<Model> {
const models: Model[] = [];
format.data.structures.forEach((_, i) => {
const model = createModel(format, i, models.length > 0 ? models[models.length - 1] : void 0);
models.push(model);
});
return models;
}
export default buildModels;
\ No newline at end of file
......@@ -9,6 +9,8 @@ import Format from './format'
import Hierarchy from './properties/hierarchy'
import Conformation from './properties/conformation'
import Symmetry from './properties/symmetry'
import from_gro from './formats/gro'
import from_mmCIF from './formats/mmcif'
......@@ -34,6 +36,7 @@ interface Model extends Readonly<{
namespace Model {
export function create(format: Format) {
switch (format.kind) {
case 'gro': return from_gro(format);
case 'mmCIF': return from_mmCIF(format);
}
}
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
const elm1 = [ 'H', 'C', 'O', 'N', 'S', 'P' ]
const elm2 = [ 'NA', 'CL', 'FE' ]
function charAtIsNumber(str: string, index: number) {
const code = str.charCodeAt(index)
return code >= 48 && code <= 57
}
export function guessElement (str: string) {
let at = str.trim().toUpperCase()
if (charAtIsNumber(at, 0)) at = at.substr(1)
// parse again to check for a second integer
if (charAtIsNumber(at, 0)) at = at.substr(1)
const n = at.length
if (n === 0) return ''
if (n === 1) return at
if (n === 2) {
if (elm2.indexOf(at) !== -1) return at
if (elm1.indexOf(at[0]) !== -1) return at[0]
}
if (n >= 3) {
if (elm1.indexOf(at[0]) !== -1) return at[0]
}
return ''
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment