diff --git a/src/mol-model/structure/export/categories/atom_site.ts b/src/mol-model/structure/export/categories/atom_site.ts index 661e00dbd300a3501f240061c6ebae620e6f6175..271f9f287c46a7573bdf2f9cae0b758df1e92077 100644 --- a/src/mol-model/structure/export/categories/atom_site.ts +++ b/src/mol-model/structure/export/categories/atom_site.ts @@ -59,4 +59,26 @@ export const _atom_site: CifCategory<CifExportContext> = { keys: () => structure.elementLocations() }; } +} + +export function residueIdFields<K, D>(getLocation: (key: K, data: D) => StructureElement): CifField<K, D>[] { + return [ + CifField.str('label_comp_id', (k, d) => P.residue.label_comp_id(getLocation(k, d))), + CifField.int('label_seq_id', (k, d) => P.residue.label_seq_id(getLocation(k, d)), { + encoder: E.deltaRLE, + valueKind: (k, d) => { + const e = getLocation(k, d); + const m = e.unit.model; + return m.atomicHierarchy.residues.label_seq_id.valueKind(m.atomicHierarchy.residueAtomSegments.index[e.element]); + } + }), + CifField.str('pdbx_PDB_ins_code', (k, d) => P.residue.pdbx_PDB_ins_code(getLocation(k, d))), + + CifField.str('label_asym_id', (k, d) => P.chain.label_asym_id(getLocation(k, d))), + CifField.str('label_entity_id', (k, d) => P.chain.label_entity_id(getLocation(k, d))), + + CifField.str('auth_comp_id', (k, d) => P.residue.auth_comp_id(getLocation(k, d))), + CifField.int('auth_seq_id', (k, d) => P.residue.auth_seq_id(getLocation(k, d)), { encoder: E.deltaRLE }), + CifField.str('auth_asym_id', (k, d) => P.chain.auth_asym_id(getLocation(k, d))), + ] } \ No newline at end of file diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index f8c9d7dcb661acc99d891efe430faeddf59c8b48..e0a071af7fd5ee59cf9093910c5513cdf909f1e6 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -98,8 +98,10 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: S encoder.writeCategory(cat, ctx); } for (const customProp of model.customProperties.all) { + const prefix = customProp.cifExport.prefix; const cats = customProp.cifExport.categories; for (const cat of cats) { + if (cat.name.indexOf(prefix) !== 0) throw new Error(`Custom category '${cat.name}' name must start with prefix '${prefix}.'`); encoder.writeCategory(cat, ctx); } } diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts index 2785d960372256cf9352a9946d4e3228ec87012b..4b7491f960e8341b148b5bbd256bc74086c41e31 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts @@ -22,6 +22,7 @@ export namespace ComponentBond { isStatic: true, name: 'chem_comp_bond', cifExport: { + prefix: '', categories: [{ name: 'chem_comp_bond', instance(ctx) { diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts b/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts index 933705171012573a68167af9657cfa80e7460fd1..371ac04a16b8410f2b256703813aa044e9a4d0a8 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts @@ -27,6 +27,7 @@ export namespace StructConn { isStatic: true, name: 'struct_conn', cifExport: { + prefix: '', categories: [{ name: 'struct_conn', instance(ctx) { diff --git a/src/mol-model/structure/model/properties/custom/descriptor.ts b/src/mol-model/structure/model/properties/custom/descriptor.ts index fd60c265dc356c5f86a44dbf370aa915794b5633..d83a32cca4e9278304637a41f7ab9f3247ffc004 100644 --- a/src/mol-model/structure/model/properties/custom/descriptor.ts +++ b/src/mol-model/structure/model/properties/custom/descriptor.ts @@ -13,6 +13,8 @@ interface ModelPropertyDescriptor<Symbols extends { [name: string]: QuerySymbolR readonly name: string, cifExport: { + // Prefix enforced during export. + prefix: string, categories: CifWriter.Category<CifExportContext>[] }, diff --git a/src/perf-tests/mol-script.ts b/src/perf-tests/mol-script.ts index 0329847e96a45993edd765f51830b29dedba7cae..f05a1d8f26b6bf547b502804da0e9321700e7c6d 100644 --- a/src/perf-tests/mol-script.ts +++ b/src/perf-tests/mol-script.ts @@ -46,7 +46,7 @@ console.log(result); const CustomProp = ModelPropertyDescriptor({ name: 'test_prop', isStatic: true, - cifExport: { categories: [ ]}, + cifExport: { prefix: '', categories: [ ]}, symbols: { residueIndex: QuerySymbolRuntime.Dynamic(CustomPropSymbol('custom.test-prop', 'residue-index', Type.Num), ctx => { const e = ctx.element; diff --git a/src/servers/model/properties.ts b/src/servers/model/properties.ts index eafa41955940a0efc940e872b67bf8ab2fb1ac58..093b01d89afa7f14e410f3c1a97d4c840e5fbc91 100644 --- a/src/servers/model/properties.ts +++ b/src/servers/model/properties.ts @@ -7,13 +7,14 @@ import { Model } from 'mol-model/structure'; import { StructureQualityReport } from './properties/structure-quality-report'; -import { SymmetryAnnotation } from './properties/rcsb/symmetry'; +// import { SymmetryAnnotation } from './properties/rcsb/symmetry'; export function attachModelProperties(model: Model): Promise<any>[] { // return a list of promises that start attaching the props in parallel // (if there are downloads etc.) return [ StructureQualityReport.attachFromPDBeApi(model), - SymmetryAnnotation.attachFromRCSB(model) + // removed for now because of schema validation error + // SymmetryAnnotation.attachFromRCSB(model) ]; } \ No newline at end of file diff --git a/src/servers/model/properties/rcsb/symmetry.ts b/src/servers/model/properties/rcsb/symmetry.ts index e34934c8d5e374ea45c1dfc8ed26ca8e75f872b1..c58f09ca9cc005b83642b3028d9336ddce7c57ff 100644 --- a/src/servers/model/properties/rcsb/symmetry.ts +++ b/src/servers/model/properties/rcsb/symmetry.ts @@ -32,9 +32,9 @@ function getCategory(name: keyof SymmetryAnnotation.Schema) { function createDatabase(assemblies: ReadonlyArray<RcsbSymmetry.Assemblies>): SymmetryAnnotation.Database { const Schema = SymmetryAnnotation.Schema - const featureRows: Table.Row<typeof Schema.symmetry_annotation_feature>[] = [] - const clusterRows: Table.Row<typeof Schema.symmetry_annotation_cluster>[] = [] - const axisRows: Table.Row<typeof Schema.symmetry_annotation_axis>[] = [] + const featureRows: Table.Row<typeof Schema.rcsb_symmetry_annotation_feature>[] = [] + const clusterRows: Table.Row<typeof Schema.rcsb_symmetry_annotation_cluster>[] = [] + const axisRows: Table.Row<typeof Schema.rcsb_symmetry_annotation_axis>[] = [] let id = 0 for (let i = 0, il = assemblies.length; i < il; ++i) { @@ -83,9 +83,9 @@ function createDatabase(assemblies: ReadonlyArray<RcsbSymmetry.Assemblies>): Sym } return _Database.ofTables('symmetry_annotation', Schema, { - symmetry_annotation_feature: Table.ofRows(Schema.symmetry_annotation_feature, featureRows), - symmetry_annotation_cluster: Table.ofRows(Schema.symmetry_annotation_cluster, clusterRows), - symmetry_annotation_axis: Table.ofRows(Schema.symmetry_annotation_axis, axisRows) + symmetry_annotation_feature: Table.ofRows(Schema.rcsb_symmetry_annotation_feature, featureRows), + symmetry_annotation_cluster: Table.ofRows(Schema.rcsb_symmetry_annotation_cluster, clusterRows), + symmetry_annotation_axis: Table.ofRows(Schema.rcsb_symmetry_annotation_axis, axisRows) }) } @@ -93,10 +93,11 @@ const _Descriptor: ModelPropertyDescriptor = { isStatic: true, name: 'symmetry_annotation', cifExport: { + prefix: 'rcsb', categories: [ - getCategory('symmetry_annotation_feature'), - getCategory('symmetry_annotation_cluster'), - getCategory('symmetry_annotation_axis') + getCategory('rcsb_symmetry_annotation_feature'), + getCategory('rcsb_symmetry_annotation_cluster'), + getCategory('rcsb_symmetry_annotation_axis') ] } } @@ -105,7 +106,7 @@ const client = new GraphQLClient('http://rest-experimental.rcsb.org/graphql') export namespace SymmetryAnnotation { export const Schema = { - symmetry_annotation_feature: { + rcsb_symmetry_annotation_feature: { id: int, assembly_id: str, source: str, @@ -113,12 +114,12 @@ export namespace SymmetryAnnotation { stoichiometry_value: List(',', x => x), stoichiometry_description: str }, - symmetry_annotation_cluster: { + rcsb_symmetry_annotation_cluster: { feature_id: int, avg_rmsd: float, members: List(',', x => x) }, - symmetry_annotation_axis: { + rcsb_symmetry_annotation_axis: { feature_id: int, order: int, start: Vector(3), diff --git a/src/servers/model/properties/structure-quality-report.ts b/src/servers/model/properties/structure-quality-report.ts index 0b75f6d7d7b86d0ed293f918a1635ffc233d4962..b65a1d959befcda3a52354f0b6fe9162e251163d 100644 --- a/src/servers/model/properties/structure-quality-report.ts +++ b/src/servers/model/properties/structure-quality-report.ts @@ -4,11 +4,12 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { ResidueIndex, ModelPropertyDescriptor, Model, Structure, Unit, StructureElement, StructureProperties as P } from 'mol-model/structure'; +import { ResidueIndex, ModelPropertyDescriptor, Model, Structure, Unit, StructureElement } from 'mol-model/structure'; import fetch from 'node-fetch'; import { CifWriter } from 'mol-io/writer/cif'; import CifField = CifWriter.Field; import { Segmentation } from 'mol-data/int'; +import { residueIdFields } from 'mol-model/structure/export/categories/atom_site'; type IssueMap = Map<ResidueIndex, string[]> @@ -16,8 +17,9 @@ const _Descriptor: ModelPropertyDescriptor = { isStatic: true, name: 'structure_quality_report', cifExport: { + prefix: 'pdbe', categories: [{ - name: 'structure_quality_report', + name: 'pdbe_structure_quality_report', instance(ctx) { const issues = StructureQualityReport.get(ctx.model); if (!issues) return CifWriter.Category.Empty; @@ -36,16 +38,8 @@ const _Descriptor: ModelPropertyDescriptor = { type ExportCtx = { model: Model, residueIndex: ArrayLike<ResidueIndex>, residues: StructureElement[], issues: IssueMap }; const _structure_quality_report_fields: CifField<ResidueIndex, ExportCtx>[] = [ - CifField.str<ResidueIndex, ExportCtx>('label_comp_id', (i, d) => P.residue.label_comp_id(d.residues[i])), - CifField.int<ResidueIndex, ExportCtx>('label_seq_id', (i, d) => P.residue.label_seq_id(d.residues[i])), - CifField.str<ResidueIndex, ExportCtx>('pdbx_PDB_ins_code', (i, d) => P.residue.pdbx_PDB_ins_code(d.residues[i])), - CifField.str<ResidueIndex, ExportCtx>('label_asym_id', (i, d) => P.chain.label_asym_id(d.residues[i])), - CifField.str<ResidueIndex, ExportCtx>('label_entity_id', (i, d) => P.entity.id(d.residues[i])), - - CifField.str<ResidueIndex, ExportCtx>('auth_comp_id', (i, d) => P.residue.auth_comp_id(d.residues[i])), - CifField.int<ResidueIndex, ExportCtx>('auth_seq_id', (i, d) => P.residue.auth_seq_id(d.residues[i])), - CifField.str<ResidueIndex, ExportCtx>('auth_asym_id', (i, d) => P.chain.auth_asym_id(d.residues[i])), - + CifField.index('id'), + ...residueIdFields<ResidueIndex, ExportCtx>((k, d) => d.residues[k]), CifField.str<ResidueIndex, ExportCtx>('issues', (i, d) => d.issues.get(d.residueIndex[d.residues[i].element])!.join(',')) ];