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

Refactoring custom model props

parent d2a17b80
No related branches found
No related tags found
No related merge requests found
......@@ -95,6 +95,11 @@ export namespace Field {
return this;
}
many(fields: ArrayLike<Field<K, D>>) {
for (let i = 0; i < fields.length; i++) this.fields.push(fields[i]);
return this;
}
getFields() { return this.fields; }
}
......
......@@ -8,7 +8,7 @@ import { Column, Table } from 'mol-data/db';
import { toTable } from 'mol-io/reader/cif/schema';
import { mmCIF_residueId_schema } from 'mol-io/reader/cif/schema/mmcif-extras';
import { CifWriter } from 'mol-io/writer/cif';
import { Model, ModelPropertyDescriptor, ResidueCustomProperty, ResidueIndex, StructureProperties as P, Unit } from 'mol-model/structure';
import { Model, ModelPropertyDescriptor, ResidueIndex, StructureProperties as P, Unit, IndexedCustomProperty } from 'mol-model/structure';
import { residueIdFields } from 'mol-model/structure/export/categories/atom_site';
import { StructureElement } from 'mol-model/structure/structure';
import { CustomPropSymbol } from 'mol-script/language/symbol';
......@@ -18,7 +18,7 @@ import { PropertyWrapper } from '../common/wrapper';
import CifField = CifWriter.Field;
export namespace StructureQualityReport {
export type IssueMap = ResidueCustomProperty<string[]>
export type IssueMap = IndexedCustomProperty.Residue<string[]>
export type Property = PropertyWrapper<IssueMap | undefined>
export function get(model: Model): Property | undefined {
......@@ -38,7 +38,7 @@ export namespace StructureQualityReport {
instance(ctx) {
return {
fields: _structure_quality_report_issues_fields,
source: ctx.structures.map(s => ResidueCustomProperty.getCifDataSource(s, StructureQualityReport.getIssueMap(s.model), ctx.cache))
source: ctx.structures.map(s => IndexedCustomProperty.getCifDataSource(s, StructureQualityReport.getIssueMap(s.model), ctx.cache))
};
}
}]
......@@ -111,13 +111,13 @@ export namespace StructureQualityReport {
}
}
type ExportCtx = ResidueCustomProperty.ExportCtx<string[]>
const _structure_quality_report_issues_fields: CifField<number, ExportCtx>[] = [
CifField.index('id'),
...residueIdFields<number, ExportCtx>((i, d) => d.elements[i]),
CifField.int<number, ExportCtx>('pdbx_PDB_model_num', (i, d) => P.unit.model_num(d.elements[i])),
CifField.str<number, ExportCtx>('issues', (i, d) => d.property(i).join(','))
];
type ExportCtx = IndexedCustomProperty.ExportCtx<string[]>
const _structure_quality_report_issues_fields: CifField<number, ExportCtx>[] = CifWriter.fields()
.index('id')
.many(residueIdFields((i, d) => d.elements[i]))
.int('pdbx_PDB_model_num', (i, d) => P.unit.model_num(d.elements[i]))
.str('issues', (i, d) => d.property(i).join(','))
.getFields()
function createIssueMapFromJson(modelData: Model, data: any): StructureQualityReport.IssueMap | undefined {
const ret = new Map<ResidueIndex, string[]>();
......@@ -140,7 +140,7 @@ function createIssueMapFromJson(modelData: Model, data: any): StructureQualityRe
}
}
return ResidueCustomProperty.fromMap(ret, Unit.Kind.Atomic);
return IndexedCustomProperty.fromResidueMap(ret, Unit.Kind.Atomic);
}
function createIssueMapFromCif(modelData: Model, data: Table<typeof StructureQualityReport.Schema.pdbe_structure_quality_report_issues>): StructureQualityReport.IssueMap | undefined {
......@@ -153,5 +153,5 @@ function createIssueMapFromCif(modelData: Model, data: Table<typeof StructureQua
ret.set(idx, issues.value(i));
}
return ResidueCustomProperty.fromMap(ret, Unit.Kind.Atomic);
return IndexedCustomProperty.fromResidueMap(ret, Unit.Kind.Atomic);
}
\ No newline at end of file
......@@ -178,6 +178,9 @@ export namespace AssemblySymmetry {
let db: Database
// TODO: there should be a "meta field" that indicates the property was added (see for example PDBe structure report)
// the reason for this is that the feature might not be present and therefore the "default" categories would
// not be created. This would result in an unnecessarily failed web request.
if (model.sourceData.kind === 'mmCIF' && model.sourceData.frame.categoryNames.includes('rcsb_assembly_symmetry_feature')) {
const rcsb_assembly_symmetry_feature = toTable(Schema.rcsb_assembly_symmetry_feature, model.sourceData.frame.categories.rcsb_assembly_symmetry_feature)
......
......@@ -6,4 +6,4 @@
export * from './custom/descriptor'
export * from './custom/collection'
export * from './custom/residue'
\ No newline at end of file
export * from './custom/indexed'
\ No newline at end of file
// /**
// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
// *
// * @author David Sehnal <david.sehnal@gmail.com>
// */
// import { ChainIndex } from '../../indexing';
// import { Unit, Structure, StructureElement } from '../../../structure';
// import { Segmentation } from 'mol-data/int';
// import { UUID } from 'mol-util';
// import { CifWriter } from 'mol-io/writer/cif';
// export interface ChainCustomProperty<T = any> {
// readonly id: UUID,
// readonly kind: Unit.Kind,
// has(idx: ChainIndex): boolean
// get(idx: ChainIndex): T | undefined
// }
// export namespace ChainCustomProperty {
// export interface ExportCtx<T> {
// elements: StructureElement[],
// property(index: number): T
// };
// function getExportCtx<T>(prop: ChainCustomProperty<T>, structure: Structure): ExportCtx<T> {
// const chainIndex = structure.model.atomicHierarchy.chainAtomSegments.index;
// const elements = getStructureElements(structure, prop);
// return { elements, property: i => prop.get(chainIndex[elements[i].element])! };
// }
// export function getCifDataSource<T>(structure: Structure, prop: ChainCustomProperty<T> | undefined, cache: any): CifWriter.Category.Instance['source'][0] {
// if (!prop) return { rowCount: 0 };
// if (cache && cache[prop.id]) return cache[prop.id];
// const data = getExportCtx(prop, structure);
// const ret = { data, rowCount: data.elements.length };
// if (cache) cache[prop.id] = ret;
// return ret;
// }
// class FromMap<T> implements ChainCustomProperty<T> {
// readonly id = UUID.create();
// has(idx: ChainIndex): boolean {
// return this.map.has(idx);
// }
// get(idx: ChainIndex) {
// return this.map.get(idx);
// }
// constructor(private map: Map<ChainIndex, T>, public kind: Unit.Kind) {
// }
// }
// export function fromMap<T>(map: Map<ChainIndex, T>, kind: Unit.Kind) {
// return new FromMap(map, kind);
// }
// /**
// * Gets all StructureElements that correspond to 1st atoms of residues that have an property assigned.
// * Only works correctly for structures with a single model.
// */
// export function getStructureElements(structure: Structure, property: ChainCustomProperty) {
// const models = structure.models;
// if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
// const seenChains = new Set<ChainIndex>();
// const unitGroups = structure.unitSymmetryGroups;
// const loci: StructureElement[] = [];
// for (const unitGroup of unitGroups) {
// const unit = unitGroup.units[0];
// if (unit.kind !== property.kind) {
// continue;
// }
// const chains = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
// while (chains.hasNext) {
// const seg = chains.move();
// if (!property.has(seg.index) || seenChains.has(seg.index)) continue;
// seenChains.add(seg.index);
// loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
// }
// }
// loci.sort((x, y) => x.element - y.element);
// return loci;
// }
// }
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { ResidueIndex, ChainIndex, ElementIndex } from '../../indexing';
import { Unit, Structure, StructureElement } from '../../../structure';
import { Segmentation } from 'mol-data/int';
import { UUID } from 'mol-util';
import { CifWriter } from 'mol-io/writer/cif';
import { Model } from '../../model';
export class IndexedCustomProperty<Idx extends IndexedCustomProperty.Index, T = any> {
readonly id: UUID = UUID.create();
readonly kind: Unit.Kind;
has(idx: Idx): boolean { return this.map.has(idx); }
get(idx: Idx) { return this.map.get(idx); }
private getStructureElements(structure: Structure) {
const models = structure.models;
if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
const seenIndices = new Set<Idx>();
const unitGroups = structure.unitSymmetryGroups;
const loci: StructureElement[] = [];
const segments = this.segmentGetter(models[0])
for (const unitGroup of unitGroups) {
const unit = unitGroup.units[0];
if (unit.kind !== this.kind) {
continue;
}
const chains = Segmentation.transientSegments(segments, unit.elements);
while (chains.hasNext) {
const seg = chains.move();
if (!this.has(seg.index) || seenIndices.has(seg.index)) continue;
seenIndices.add(seg.index);
loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
}
}
loci.sort((x, y) => x.element - y.element);
return loci;
}
getExportContext(structure: Structure): IndexedCustomProperty.ExportCtx<T> {
const index = this.segmentGetter(structure.model).index;
const elements = this.getStructureElements(structure);
return { elements, property: i => this.get(index[elements[i].element])! };
}
constructor(private map: Map<Idx, T>, private segmentGetter: (model: Model) => Segmentation<ElementIndex, Idx>, kind: Unit.Kind) {
this.kind = kind;
}
}
export namespace IndexedCustomProperty {
export type Index = ResidueIndex | ChainIndex
export interface ExportCtx<T> {
elements: StructureElement[],
property(index: number): T
}
export function getCifDataSource<Idx extends Index, T>(structure: Structure, prop: IndexedCustomProperty<Idx, T> | undefined, cache: any): CifWriter.Category.Instance['source'][0] {
if (!prop) return { rowCount: 0 };
if (cache && cache[prop.id]) return cache[prop.id];
const data = prop.getExportContext(structure);
const ret = { data, rowCount: data.elements.length };
if (cache) cache[prop.id] = ret;
return ret;
}
export type Residue<T> = IndexedCustomProperty<ResidueIndex, T>
const getResidueSegments = (model: Model) => model.atomicHierarchy.residueAtomSegments;
export function fromResidueMap<T>(map: Map<ResidueIndex, T>, kind: Unit.Kind): Residue<T> {
return new IndexedCustomProperty(map, getResidueSegments, kind);
}
export type Chain<T> = IndexedCustomProperty<ChainIndex, T>
const getChainSegments = (model: Model) => model.atomicHierarchy.chainAtomSegments;
export function fromChainMap<T>(map: Map<ChainIndex, T>, kind: Unit.Kind): Chain<T> {
return new IndexedCustomProperty(map, getChainSegments, kind);
}
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { ResidueIndex } from '../../indexing';
import { Unit, Structure, StructureElement } from '../../../structure';
import { Segmentation } from 'mol-data/int';
import { UUID } from 'mol-util';
import { CifWriter } from 'mol-io/writer/cif';
export interface ResidueCustomProperty<T = any> {
readonly id: UUID,
readonly kind: Unit.Kind,
has(idx: ResidueIndex): boolean
get(idx: ResidueIndex): T | undefined
}
export namespace ResidueCustomProperty {
export interface ExportCtx<T> {
elements: StructureElement[],
property(index: number): T
};
function getExportCtx<T>(prop: ResidueCustomProperty<T>, structure: Structure): ExportCtx<T> {
const residueIndex = structure.model.atomicHierarchy.residueAtomSegments.index;
const elements = getStructureElements(structure, prop);
return { elements, property: i => prop.get(residueIndex[elements[i].element])! };
}
export function getCifDataSource<T>(structure: Structure, prop: ResidueCustomProperty<T> | undefined, cache: any): CifWriter.Category.Instance['source'][0] {
if (!prop) return { rowCount: 0 };
if (cache && cache[prop.id]) return cache[prop.id];
const data = getExportCtx(prop, structure);
const ret = { data, rowCount: data.elements.length };
if (cache) cache[prop.id] = ret;
return ret;
}
class FromMap<T> implements ResidueCustomProperty<T> {
readonly id = UUID.create();
has(idx: ResidueIndex): boolean {
return this.map.has(idx);
}
get(idx: ResidueIndex) {
return this.map.get(idx);
}
constructor(private map: Map<ResidueIndex, T>, public kind: Unit.Kind) {
}
}
export function fromMap<T>(map: Map<ResidueIndex, T>, kind: Unit.Kind) {
return new FromMap(map, kind);
}
/**
* Gets all StructureElements that correspond to 1st atoms of residues that have an property assigned.
* Only works correctly for structures with a single model.
*/
export function getStructureElements(structure: Structure, property: ResidueCustomProperty) {
const models = structure.models;
if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
const seenResidues = new Set<ResidueIndex>();
const unitGroups = structure.unitSymmetryGroups;
const loci: StructureElement[] = [];
for (const unitGroup of unitGroups) {
const unit = unitGroup.units[0];
if (unit.kind !== property.kind) {
continue;
}
const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
while (residues.hasNext) {
const seg = residues.move();
if (!property.has(seg.index) || seenResidues.has(seg.index)) continue;
seenResidues.add(seg.index);
loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
}
}
loci.sort((x, y) => x.element - y.element);
return loci;
}
}
\ No newline at end of file
// /**
// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
// *
// * @author David Sehnal <david.sehnal@gmail.com>
// */
// import { ResidueIndex } from '../../indexing';
// import { Unit, Structure, StructureElement } from '../../../structure';
// import { Segmentation } from 'mol-data/int';
// import { UUID } from 'mol-util';
// import { CifWriter } from 'mol-io/writer/cif';
// export interface ResidueCustomProperty<T = any> {
// readonly id: UUID,
// readonly kind: Unit.Kind,
// has(idx: ResidueIndex): boolean
// get(idx: ResidueIndex): T | undefined
// }
// export namespace ResidueCustomProperty {
// export interface ExportCtx<T> {
// elements: StructureElement[],
// property(index: number): T
// };
// function getExportCtx<T>(prop: ResidueCustomProperty<T>, structure: Structure): ExportCtx<T> {
// const residueIndex = structure.model.atomicHierarchy.residueAtomSegments.index;
// const elements = getStructureElements(structure, prop);
// return { elements, property: i => prop.get(residueIndex[elements[i].element])! };
// }
// export function getCifDataSource<T>(structure: Structure, prop: ResidueCustomProperty<T> | undefined, cache: any): CifWriter.Category.Instance['source'][0] {
// if (!prop) return { rowCount: 0 };
// if (cache && cache[prop.id]) return cache[prop.id];
// const data = getExportCtx(prop, structure);
// const ret = { data, rowCount: data.elements.length };
// if (cache) cache[prop.id] = ret;
// return ret;
// }
// class FromMap<T> implements ResidueCustomProperty<T> {
// readonly id = UUID.create();
// has(idx: ResidueIndex): boolean {
// return this.map.has(idx);
// }
// get(idx: ResidueIndex) {
// return this.map.get(idx);
// }
// constructor(private map: Map<ResidueIndex, T>, public kind: Unit.Kind) {
// }
// }
// export function fromMap<T>(map: Map<ResidueIndex, T>, kind: Unit.Kind) {
// return new FromMap(map, kind);
// }
// /**
// * Gets all StructureElements that correspond to 1st atoms of residues that have an property assigned.
// * Only works correctly for structures with a single model.
// */
// export function getStructureElements(structure: Structure, property: ResidueCustomProperty) {
// const models = structure.models;
// if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
// const seenResidues = new Set<ResidueIndex>();
// const unitGroups = structure.unitSymmetryGroups;
// const loci: StructureElement[] = [];
// for (const unitGroup of unitGroups) {
// const unit = unitGroup.units[0];
// if (unit.kind !== property.kind) {
// continue;
// }
// const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
// while (residues.hasNext) {
// const seg = residues.move();
// if (!property.has(seg.index) || seenResidues.has(seg.index)) continue;
// seenResidues.add(seg.index);
// loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
// }
// }
// loci.sort((x, y) => x.element - y.element);
// return loci;
// }
// }
\ No newline at end of file
......@@ -55,6 +55,14 @@ async function run() {
// atom_site: { label_comp_id: 'ALA' }
// }
// });
// const request = createJob({
// entryId: path.join(examplesPath, testFile),
// queryName: 'residueInteraction',
// queryParams: {
// atom_site: { label_comp_id: 'REA' },
// radius: 5
// }
// });
const encoder = await resolveJob(request);
const writer = wrapFile(path.join(outPath, testFile));
encoder.writeTo(writer);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment