|
@@ -17,6 +17,7 @@ import { Tensor } from 'mol-math/linear-algebra';
|
|
|
import { CifExportContext } from 'mol-model/structure/export/mmcif';
|
|
|
import { toTable } from 'mol-io/reader/cif/schema';
|
|
|
import { CifCategory } from 'mol-io/reader/cif';
|
|
|
+import { PropertyWrapper } from 'mol-model-props/common/wrapper';
|
|
|
|
|
|
const { str, int, float, Aliased, Vector, List } = Column.Schema;
|
|
|
|
|
@@ -31,51 +32,58 @@ function getCategory(name: keyof AssemblySymmetry.Schema) {
|
|
|
return { name, instance: getInstance(name) }
|
|
|
}
|
|
|
|
|
|
-function createDatabase(assemblies: ReadonlyArray<AssemblySymmetryGraphQL.Assemblies>): AssemblySymmetry.Database {
|
|
|
+function createDatabaseFromJson(assemblies: ReadonlyArray<AssemblySymmetryGraphQL.Assemblies>): AssemblySymmetry.Database {
|
|
|
const Schema = AssemblySymmetry.Schema
|
|
|
|
|
|
- const featureRows: Table.Row<typeof Schema.rcsb_assembly_symmetry_feature>[] = []
|
|
|
+ const featureRows: Table.Row<typeof Schema.rcsb_assembly_symmetry>[] = []
|
|
|
const clusterRows: Table.Row<typeof Schema.rcsb_assembly_symmetry_cluster>[] = []
|
|
|
+ const clusterMemberRows: Table.Row<typeof Schema.rcsb_assembly_symmetry_cluster_member>[] = []
|
|
|
const axisRows: Table.Row<typeof Schema.rcsb_assembly_symmetry_axis>[] = []
|
|
|
|
|
|
let id = 1 // start feature ids at 1
|
|
|
for (let i = 0, il = assemblies.length; i < il; ++i) {
|
|
|
- const { assembly_id: _assembly_id, rcsb_assembly_symmetry } = assemblies[i]
|
|
|
- if (!rcsb_assembly_symmetry) continue
|
|
|
- const assembly_id = _assembly_id.toString() // TODO type will be fixed to string upstream
|
|
|
- const source = rcsb_assembly_symmetry.source
|
|
|
- const symmetry_features = rcsb_assembly_symmetry.symmetry_features
|
|
|
- for (let j = 0, jl = symmetry_features.length; j < jl; ++j) {
|
|
|
- const f = symmetry_features[j]! // TODO upstream, array members should not be nullable
|
|
|
+ const { pdbx_struct_assembly, rcsb_struct_symmetry, rcsb_struct_symmetry_provenance } = assemblies[i]
|
|
|
+ if (!pdbx_struct_assembly || !rcsb_struct_symmetry ||!rcsb_struct_symmetry_provenance) continue
|
|
|
+ const assembly_id = pdbx_struct_assembly.id
|
|
|
+ for (let j = 0, jl = rcsb_struct_symmetry.length; j < jl; ++j) {
|
|
|
+ const rss = rcsb_struct_symmetry[j]! // TODO upstream, array members should not be nullable
|
|
|
featureRows.push({
|
|
|
id,
|
|
|
assembly_id,
|
|
|
- source,
|
|
|
- type: f.type,
|
|
|
- stoichiometry_value: f.stoichiometry.value as string[], // TODO upstream, array members should not be nullable
|
|
|
- stoichiometry_description: f.stoichiometry.description,
|
|
|
- symmetry_value: f.symmetry.value,
|
|
|
- symmetry_description: f.symmetry.description
|
|
|
+ provenance: rcsb_struct_symmetry_provenance,
|
|
|
+ type: rss.type,
|
|
|
+ stoichiometry: rss.stoichiometry as string[], // TODO upstream, array members should not be nullable
|
|
|
+ kind: rss.kind,
|
|
|
+ symbol: rss.symbol,
|
|
|
+ oligomeric_state: rss.oligomeric_state
|
|
|
})
|
|
|
|
|
|
- const clusters = f.clusters
|
|
|
+ const clusters = rss.clusters
|
|
|
if (clusters) {
|
|
|
for (let k = 0, kl = clusters.length; k < kl; ++k) {
|
|
|
const c = clusters[k]! // TODO upstream, array members should not be nullable
|
|
|
clusterRows.push({
|
|
|
- feature_id: id,
|
|
|
+ id: k,
|
|
|
+ symmetry_id: id,
|
|
|
avg_rmsd: c.avg_rmsd || 0, // TODO upstream, should not be nullable, or???
|
|
|
- members: c.members as string[] // TODO upstream, array members should not be nullable
|
|
|
})
|
|
|
+ for (let l = 0, ll = c.members.length; l < ll; ++l) {
|
|
|
+ const m = c.members[l]! // TODO upstream, array members should not be nullable
|
|
|
+ clusterMemberRows.push({
|
|
|
+ cluster_id: k,
|
|
|
+ asym_id: m.asym_id,
|
|
|
+ pdbx_struct_oper_list_ids: (m.pdbx_struct_oper_list_ids || []) as string[]
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const axes = f.symmetry_axes
|
|
|
+ const axes = rss.rotation_axes
|
|
|
if (axes) {
|
|
|
for (let k = 0, kl = axes.length; k < kl; ++k) {
|
|
|
const a = axes[k]!
|
|
|
axisRows.push({
|
|
|
- feature_id: id,
|
|
|
+ symmetry_id: id,
|
|
|
order: a.order!, // TODO upstream, should not be nullable, or???
|
|
|
start: a.start as Tensor.Data, // TODO upstream, array members should not be nullable
|
|
|
end: a.end as Tensor.Data // TODO upstream, array members should not be nullable
|
|
@@ -88,20 +96,57 @@ function createDatabase(assemblies: ReadonlyArray<AssemblySymmetryGraphQL.Assemb
|
|
|
}
|
|
|
|
|
|
return _Database.ofTables('assembly_symmetry', Schema, {
|
|
|
- rcsb_assembly_symmetry_feature: Table.ofRows(Schema.rcsb_assembly_symmetry_feature, featureRows),
|
|
|
+ rcsb_assembly_symmetry: Table.ofRows(Schema.rcsb_assembly_symmetry, featureRows),
|
|
|
rcsb_assembly_symmetry_cluster: Table.ofRows(Schema.rcsb_assembly_symmetry_cluster, clusterRows),
|
|
|
+ rcsb_assembly_symmetry_cluster_member: Table.ofRows(Schema.rcsb_assembly_symmetry_cluster_member, clusterMemberRows),
|
|
|
rcsb_assembly_symmetry_axis: Table.ofRows(Schema.rcsb_assembly_symmetry_axis, axisRows)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+function createDatabaseFromCif(model: Model): AssemblySymmetry.Database {
|
|
|
+ const Schema = AssemblySymmetry.Schema
|
|
|
+
|
|
|
+ const rcsb_assembly_symmetry = toTable(Schema.rcsb_assembly_symmetry, model.sourceData.frame.categories.rcsb_assembly_symmetry_feature)
|
|
|
+
|
|
|
+ let rcsb_assembly_symmetry_cluster
|
|
|
+ if (model.sourceData.frame.categoryNames.includes('rcsb_assembly_symmetry_cluster')) {
|
|
|
+ rcsb_assembly_symmetry_cluster = toTable(Schema.rcsb_assembly_symmetry_cluster, model.sourceData.frame.categories.rcsb_assembly_symmetry_cluster)
|
|
|
+ } else {
|
|
|
+ rcsb_assembly_symmetry_cluster = CifCategory.empty
|
|
|
+ }
|
|
|
+
|
|
|
+ let rcsb_assembly_symmetry_cluster_member
|
|
|
+ if (model.sourceData.frame.categoryNames.includes('rcsb_assembly_symmetry_cluster_member')) {
|
|
|
+ rcsb_assembly_symmetry_cluster_member = toTable(Schema.rcsb_assembly_symmetry_cluster_member, model.sourceData.frame.categories.rcsb_assembly_symmetry_cluster_member)
|
|
|
+ } else {
|
|
|
+ rcsb_assembly_symmetry_cluster_member = CifCategory.empty
|
|
|
+ }
|
|
|
+
|
|
|
+ let rcsb_assembly_symmetry_axis
|
|
|
+ if (model.sourceData.frame.categoryNames.includes('rcsb_assembly_symmetry_axis')) {
|
|
|
+ rcsb_assembly_symmetry_axis = toTable(Schema.rcsb_assembly_symmetry_axis, model.sourceData.frame.categories.rcsb_assembly_symmetry_axis)
|
|
|
+ } else {
|
|
|
+ rcsb_assembly_symmetry_axis = CifCategory.empty
|
|
|
+ }
|
|
|
+
|
|
|
+ return _Database.ofTables('rcsb_assembly_symmetry', Schema, {
|
|
|
+ rcsb_assembly_symmetry,
|
|
|
+ rcsb_assembly_symmetry_cluster,
|
|
|
+ rcsb_assembly_symmetry_cluster_member,
|
|
|
+ rcsb_assembly_symmetry_axis
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
const _Descriptor: ModelPropertyDescriptor = {
|
|
|
isStatic: true,
|
|
|
- name: 'assembly_symmetry',
|
|
|
+ name: 'rcsb_assembly_symmetry',
|
|
|
cifExport: {
|
|
|
prefix: 'rcsb',
|
|
|
categories: [
|
|
|
- getCategory('rcsb_assembly_symmetry_feature'),
|
|
|
+ PropertyWrapper.defaultInfoCategory<CifExportContext>('rcsb_assembly_symmetry_info', ctx => PropertyWrapper.createInfo()),
|
|
|
+ getCategory('rcsb_assembly_symmetry'),
|
|
|
getCategory('rcsb_assembly_symmetry_cluster'),
|
|
|
+ getCategory('rcsb_assembly_symmetry_cluster_member'),
|
|
|
getCategory('rcsb_assembly_symmetry_axis')
|
|
|
]
|
|
|
}
|
|
@@ -111,56 +156,85 @@ const client = new GraphQLClient('http://rest-experimental.rcsb.org/graphql')
|
|
|
|
|
|
export interface AssemblySymmetry {
|
|
|
db: AssemblySymmetry.Database
|
|
|
- getFeatures(assemblyId: string): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry_feature']>
|
|
|
- getClusters(featureId: number): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry_cluster']>
|
|
|
- getAxes(featureId: number): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry_axis']>
|
|
|
+ getSymmetries(assemblyId: string): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry']>
|
|
|
+ getClusters(symmetryId: number): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry_cluster']>
|
|
|
+ getClusterMembers(clusterId: number): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry_cluster_member']>
|
|
|
+ getAxes(symmetryId: number): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry_axis']>
|
|
|
}
|
|
|
|
|
|
export function AssemblySymmetry(db: AssemblySymmetry.Database): AssemblySymmetry {
|
|
|
- const f = db.rcsb_assembly_symmetry_feature
|
|
|
+ const f = db.rcsb_assembly_symmetry
|
|
|
const c = db.rcsb_assembly_symmetry_cluster
|
|
|
+ const cm = db.rcsb_assembly_symmetry_cluster_member
|
|
|
const a = db.rcsb_assembly_symmetry_axis
|
|
|
|
|
|
return {
|
|
|
db,
|
|
|
- getFeatures: (assemblyId: string) => Table.pick(f, f._schema, i => f.assembly_id.value(i) === assemblyId),
|
|
|
- getClusters: (featureId: number) => Table.pick(c, c._schema, i => c.feature_id.value(i) === featureId),
|
|
|
- getAxes: (featureId: number) => Table.pick(a, a._schema, i => a.feature_id.value(i) === featureId)
|
|
|
+ getSymmetries: (assemblyId: string) => Table.pick(f, f._schema, i => f.assembly_id.value(i) === assemblyId),
|
|
|
+ getClusters: (symmetryId: number) => Table.pick(c, c._schema, i => c.symmetry_id.value(i) === symmetryId),
|
|
|
+ getClusterMembers: (clusterId: number) => Table.pick(cm, cm._schema, i => cm.cluster_id.value(i) === clusterId),
|
|
|
+ getAxes: (symmetryId: number) => Table.pick(a, a._schema, i => a.symmetry_id.value(i) === symmetryId)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
export namespace AssemblySymmetry {
|
|
|
export const Schema = {
|
|
|
- rcsb_assembly_symmetry_feature: {
|
|
|
- /** Uniquely identifies a record in `rcsb_assembly_symmetry_feature` */
|
|
|
+ rcsb_assembly_symmetry_info: {
|
|
|
+ updated_datetime_utc: Column.Schema.str
|
|
|
+ },
|
|
|
+ rcsb_assembly_symmetry: {
|
|
|
+ /** Uniquely identifies a record in `rcsb_assembly_symmetry` */
|
|
|
id: int,
|
|
|
- /** A pointer to `pdbx_struct_assembly.id` */
|
|
|
+ /**
|
|
|
+ * A pointer to `pdbx_struct_assembly.id`.
|
|
|
+ * The value 'deposited' refers to the coordinates as given in the file.
|
|
|
+ * */
|
|
|
assembly_id: str,
|
|
|
- /** Name and version of software used to calculate protein symmetry */
|
|
|
- source: str,
|
|
|
+ /** Name and version of software used to calculate assembly symmetry */
|
|
|
+ provenance: str,
|
|
|
/** Type of protein symmetry */
|
|
|
- type: Aliased<'GLOBAL' | 'LOCAL' | 'PSEUDO'>(str),
|
|
|
+ kind: Aliased<'GLOBAL' | 'LOCAL' | 'PSEUDO'>(str),
|
|
|
/** Quantitative description of every individual subunit in a given protein */
|
|
|
- stoichiometry_value: List(',', x => x),
|
|
|
- /** Oligomeric state for a given composition of subunits */
|
|
|
- stoichiometry_description: str,
|
|
|
- /** Point group symmetry value */
|
|
|
- symmetry_value: str,
|
|
|
- /** Point group symmetry description */
|
|
|
- symmetry_description: str
|
|
|
+ stoichiometry: List(',', x => x),
|
|
|
+ /**
|
|
|
+ * Symmetry symbol refers to point group or helical symmetry of identical subunits.
|
|
|
+ * Contains point group symbol (e.g., C2, C5, D2, T, O, I) or H for helical symmetry.
|
|
|
+ */
|
|
|
+ symbol: str,
|
|
|
+ /** Point group or helical symmetry */
|
|
|
+ type: Aliased<'ASYMMETRIC' | 'CYCLIC' | 'DIHEDRAL' | 'HELICAL' | 'ICOSAHEDRAL' | 'OCTAHEDRAL' | 'TETRAHEDRAL'>(str),
|
|
|
+ /**
|
|
|
+ * Oligomeric state refers to a composition of subunits in quaternary structure.
|
|
|
+ * Quaternary structure may be composed either exclusively of several copies of identical
|
|
|
+ * subunits, in which case they are termed homo-oligomers, or alternatively by at least
|
|
|
+ * one copy of different subunits (hetero-oligomers). Quaternary structure composed of
|
|
|
+ * a single subunit is demoted as 'Monomer'.
|
|
|
+ */
|
|
|
+ oligomeric_state: str,
|
|
|
},
|
|
|
rcsb_assembly_symmetry_cluster: {
|
|
|
- /** A pointer to `rcsb_assembly_symmetry_feature.id` */
|
|
|
- feature_id: int,
|
|
|
+ /** Uniquely identifies a record in `rcsb_assembly_symmetry_cluster` */
|
|
|
+ id: int,
|
|
|
+ /** A pointer to `rcsb_assembly_symmetry.id` */
|
|
|
+ symmetry_id: int,
|
|
|
/** Average RMSD between members of a given cluster */
|
|
|
- avg_rmsd: float,
|
|
|
- /** List of `auth_label_id` values */
|
|
|
- members: List(',', x => x)
|
|
|
+ avg_rmsd: float
|
|
|
+ },
|
|
|
+ rcsb_assembly_symmetry_cluster_member: {
|
|
|
+ /** A pointer to `rcsb_assembly_symmetry_cluster.id` */
|
|
|
+ cluster_id: int,
|
|
|
+ /** The `label_asym_id` value of the member */
|
|
|
+ asym_id: str,
|
|
|
+ /** List of `pdbx_struct_oper_list_id` values of the member */
|
|
|
+ pdbx_struct_oper_list_ids: List(',', x => x)
|
|
|
},
|
|
|
rcsb_assembly_symmetry_axis: {
|
|
|
- /** A pointer to `rcsb_assembly_symmetry_feature.id` */
|
|
|
- feature_id: int,
|
|
|
- /** The order of the symmetry axis */
|
|
|
+ /** A pointer to `rcsb_assembly_symmetry.id` */
|
|
|
+ symmetry_id: int,
|
|
|
+ /**
|
|
|
+ * The number of times (order of rotation) that a subunit can be repeated by a rotation
|
|
|
+ * operation, being transformed into a new state indistinguishable from its starting state.
|
|
|
+ */
|
|
|
order: int,
|
|
|
/** The x,y,z coordinate of the start point of a symmetry axis */
|
|
|
start: Vector(3),
|
|
@@ -177,32 +251,9 @@ export namespace AssemblySymmetry {
|
|
|
if (model.customProperties.has(Descriptor)) return true;
|
|
|
|
|
|
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)
|
|
|
-
|
|
|
- let rcsb_assembly_symmetry_cluster
|
|
|
- if (model.sourceData.frame.categoryNames.includes('rcsb_assembly_symmetry_cluster')) {
|
|
|
- rcsb_assembly_symmetry_cluster = toTable(Schema.rcsb_assembly_symmetry_cluster, model.sourceData.frame.categories.rcsb_assembly_symmetry_cluster)
|
|
|
- } else {
|
|
|
- rcsb_assembly_symmetry_cluster = CifCategory.empty
|
|
|
- }
|
|
|
-
|
|
|
- let rcsb_assembly_symmetry_axis
|
|
|
- if (model.sourceData.frame.categoryNames.includes('rcsb_assembly_symmetry_axis')) {
|
|
|
- rcsb_assembly_symmetry_axis = toTable(Schema.rcsb_assembly_symmetry_axis, model.sourceData.frame.categories.rcsb_assembly_symmetry_axis)
|
|
|
- } else {
|
|
|
- rcsb_assembly_symmetry_axis = CifCategory.empty
|
|
|
- }
|
|
|
-
|
|
|
- db = _Database.ofTables('assembly_symmetry', Schema, {
|
|
|
- rcsb_assembly_symmetry_feature,
|
|
|
- rcsb_assembly_symmetry_cluster,
|
|
|
- rcsb_assembly_symmetry_axis
|
|
|
- })
|
|
|
+ let info = PropertyWrapper.tryGetInfoFromCif('rcsb_assembly_symmetry_info', model);
|
|
|
+ if (info) {
|
|
|
+ db = createDatabaseFromCif(model)
|
|
|
} else {
|
|
|
let result: AssemblySymmetryGraphQL.Query
|
|
|
const variables: AssemblySymmetryGraphQL.Variables = { pdbId: model.label.toLowerCase() };
|
|
@@ -214,15 +265,15 @@ export namespace AssemblySymmetry {
|
|
|
}
|
|
|
if (!result || !result.assemblies) return false;
|
|
|
|
|
|
- db = createDatabase(result.assemblies as ReadonlyArray<AssemblySymmetryGraphQL.Assemblies>)
|
|
|
+ db = createDatabaseFromJson(result.assemblies as ReadonlyArray<AssemblySymmetryGraphQL.Assemblies>)
|
|
|
}
|
|
|
|
|
|
model.customProperties.add(Descriptor);
|
|
|
- model._staticPropertyData.__AssemblySymmetry__ = AssemblySymmetry(db);
|
|
|
+ model._staticPropertyData.__RCSBAssemblySymmetry__ = AssemblySymmetry(db);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
export function get(model: Model): AssemblySymmetry | undefined {
|
|
|
- return model._staticPropertyData.__AssemblySymmetry__;
|
|
|
+ return model._staticPropertyData.__RCSBAssemblySymmetry__;
|
|
|
}
|
|
|
}
|