cif-core.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Column, Table } from '../../mol-data/db';
  7. import { Model, Symmetry } from '../../mol-model/structure/model';
  8. import { MoleculeType } from '../../mol-model/structure/model/types';
  9. import { RuntimeContext, Task } from '../../mol-task';
  10. import { createModels } from './basic/parser';
  11. import { BasicSchema, createBasic } from './basic/schema';
  12. import { ComponentBuilder } from './common/component';
  13. import { EntityBuilder } from './common/entity';
  14. import { ModelFormat } from './format';
  15. import { CifCore_Database } from '../../mol-io/reader/cif/schema/cif-core';
  16. import { CifFrame, CIF } from '../../mol-io/reader/cif';
  17. import { Spacegroup, SpacegroupCell } from '../../mol-math/geometry';
  18. import { Vec3 } from '../../mol-math/linear-algebra';
  19. import { ModelSymmetry } from './property/symmetry';
  20. import { IndexPairBonds } from './property/bonds/index-pair';
  21. import { AtomSiteAnisotrop } from './property/anisotropic';
  22. function getSpacegroupNameOrNumber(space_group: CifCore_Database['space_group']) {
  23. const groupNumber = space_group.IT_number.value(0)
  24. const groupName = space_group['name_H-M_full'].value(0)
  25. if (!space_group.IT_number.isDefined) return groupName
  26. if (!space_group['name_H-M_full'].isDefined) return groupNumber
  27. return groupNumber
  28. }
  29. function getSymmetry(db: CifCore_Database): Symmetry {
  30. const { cell, space_group } = db
  31. const nameOrNumber = getSpacegroupNameOrNumber(space_group)
  32. const spaceCell = SpacegroupCell.create(nameOrNumber,
  33. Vec3.create(cell.length_a.value(0), cell.length_b.value(0), cell.length_c.value(0)),
  34. Vec3.scale(Vec3.zero(), Vec3.create(cell.angle_alpha.value(0), cell.angle_beta.value(0), cell.angle_gamma.value(0)), Math.PI / 180));
  35. return {
  36. spacegroup: Spacegroup.create(spaceCell),
  37. assemblies : [],
  38. isNonStandardCrytalFrame: false,
  39. ncsOperators: []
  40. }
  41. }
  42. async function getModels(db: CifCore_Database, format: CifCoreFormat, ctx: RuntimeContext): Promise<Model[]> {
  43. const atomCount = db.atom_site._rowCount
  44. const MOL = Column.ofConst('MOL', atomCount, Column.Schema.str);
  45. const A = Column.ofConst('A', atomCount, Column.Schema.str);
  46. const seq_id = Column.ofConst(1, atomCount, Column.Schema.int);
  47. const symmetry = getSymmetry(db)
  48. const m = symmetry.spacegroup.cell.fromFractional
  49. const { fract_x, fract_y, fract_z } = db.atom_site
  50. const x = new Float32Array(atomCount)
  51. const y = new Float32Array(atomCount)
  52. const z = new Float32Array(atomCount)
  53. const v = Vec3()
  54. for (let i = 0; i < atomCount; ++i) {
  55. Vec3.set(v, fract_x.value(i), fract_y.value(i), fract_z.value(i))
  56. Vec3.transformMat4(v, v, m)
  57. x[i] = v[0], y[i] = v[1], z[i] = v[2]
  58. }
  59. const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, {
  60. auth_asym_id: A,
  61. auth_atom_id: db.atom_site.label,
  62. auth_comp_id: MOL,
  63. auth_seq_id: seq_id,
  64. Cartn_x: Column.ofFloatArray(x),
  65. Cartn_y: Column.ofFloatArray(y),
  66. Cartn_z: Column.ofFloatArray(z),
  67. id: Column.range(0, atomCount - 1),
  68. label_asym_id: A,
  69. label_atom_id: db.atom_site.label,
  70. label_comp_id: MOL,
  71. label_seq_id: seq_id,
  72. label_entity_id: Column.ofConst('1', atomCount, Column.Schema.str),
  73. occupancy: db.atom_site.occupancy,
  74. type_symbol: db.atom_site.type_symbol,
  75. pdbx_PDB_model_num: Column.ofConst(1, atomCount, Column.Schema.int),
  76. }, atomCount);
  77. const name = (
  78. db.chemical.name_common.value(0) ||
  79. db.chemical.name_systematic.value(0) ||
  80. db.chemical_formula.sum.value(0)
  81. )
  82. const entityBuilder = new EntityBuilder()
  83. entityBuilder.setNames([['MOL', name || 'Unknown Entity']])
  84. entityBuilder.getEntityId('MOL', MoleculeType.Unknown, 'A');
  85. const componentBuilder = new ComponentBuilder(seq_id, db.atom_site.type_symbol);
  86. componentBuilder.setNames([['MOL', name || 'Unknown Molecule']])
  87. componentBuilder.add('MOL', 0);
  88. const basics = createBasic({
  89. entity: entityBuilder.getEntityTable(),
  90. chem_comp: componentBuilder.getChemCompTable(),
  91. atom_site
  92. });
  93. const models = await createModels(basics, format, ctx);
  94. if (models.length > 0) {
  95. ModelSymmetry.Provider.set(models[0], symmetry)
  96. const bondCount = db.geom_bond._rowCount
  97. if(bondCount > 0) {
  98. const labelIndexMap: { [label: string]: number } = {}
  99. const { label } = db.atom_site
  100. for (let i = 0, il = label.rowCount; i < il; ++i) {
  101. labelIndexMap[label.value(i)] = i
  102. }
  103. const indexA: number[] = []
  104. const indexB: number[] = []
  105. const order: number[] = []
  106. const symmetryA: string[] = []
  107. const symmetryB: string[] = []
  108. const { atom_site_label_1, atom_site_label_2, valence, site_symmetry_1, site_symmetry_2 } = db.geom_bond
  109. for (let i = 0; i < bondCount; ++i) {
  110. indexA[i] = labelIndexMap[atom_site_label_1.value(i)]
  111. indexB[i] = labelIndexMap[atom_site_label_2.value(i)]
  112. // TODO derive order from bond length if undefined
  113. order[i] = valence.isDefined ? valence.value(i) : 1
  114. symmetryA[i] = site_symmetry_1.value(i) || '1_555'
  115. symmetryB[i] = site_symmetry_2.value(i) || '1_555'
  116. }
  117. IndexPairBonds.Provider.set(models[0], IndexPairBonds.fromData({ pairs: {
  118. indexA: Column.ofIntArray(indexA),
  119. indexB: Column.ofIntArray(indexB),
  120. order: Column.ofIntArray(order),
  121. symmetryA: Column.ofStringArray(symmetryA),
  122. symmetryB: Column.ofStringArray(symmetryB)
  123. }, count: indexA.length }));
  124. }
  125. }
  126. return models;
  127. }
  128. function atomSiteAnisotropFromCifCore(model: Model) {
  129. if (!CifCoreFormat.is(model.sourceData)) return;
  130. const { atom_site, atom_site_aniso } = model.sourceData.data.db
  131. const data = Table.ofPartialColumns(AtomSiteAnisotrop.Schema, {
  132. U: atom_site_aniso.U,
  133. U_esd: atom_site_aniso.U_su
  134. }, atom_site_aniso._rowCount);
  135. const elementToAnsiotrop = AtomSiteAnisotrop.getElementToAnsiotropFromLabel(atom_site.label, atom_site_aniso.label)
  136. return { data, elementToAnsiotrop }
  137. }
  138. function atomSiteAnisotropApplicableCifCore(model: Model) {
  139. if (!CifCoreFormat.is(model.sourceData)) return false;
  140. return model.sourceData.data.db.atom_site_aniso.U.isDefined
  141. }
  142. AtomSiteAnisotrop.Provider.formatRegistry.add('cifCore', atomSiteAnisotropFromCifCore, atomSiteAnisotropApplicableCifCore)
  143. //
  144. export { CifCoreFormat };
  145. type CifCoreFormat = ModelFormat<CifCoreFormat.Data>
  146. namespace CifCoreFormat {
  147. export type Data = { db: CifCore_Database, frame: CifFrame }
  148. export function is(x: ModelFormat): x is CifCoreFormat {
  149. return x.kind === 'cifCore'
  150. }
  151. export function fromFrame(frame: CifFrame, db?: CifCore_Database): CifCoreFormat {
  152. if (!db) db = CIF.schema.cifCore(frame)
  153. const name = (
  154. db.database_code.depnum_ccdc_archive.value(0) ||
  155. db.database_code.depnum_ccdc_fiz.value(0) ||
  156. db.database_code.ICSD.value(0) ||
  157. db.database_code.MDF.value(0) ||
  158. db.database_code.NBS.value(0) ||
  159. db.database_code.CSD.value(0) ||
  160. db.database_code.COD.value(0) ||
  161. db._name
  162. )
  163. return { kind: 'cifCore', name, data: { db, frame } };
  164. }
  165. }
  166. export function trajectoryFromCifCore(frame: CifFrame): Task<Model.Trajectory> {
  167. const format = CifCoreFormat.fromFrame(frame)
  168. return Task.create('Parse CIF Core', ctx => getModels(format.data.db, format, ctx))
  169. }