cif-core.ts 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. import { guessElementSymbolString } from './util';
  23. function getSpacegroupNameOrNumber(space_group: CifCore_Database['space_group']) {
  24. const groupNumber = space_group.IT_number.value(0);
  25. const groupName = space_group['name_H-M_full'].value(0);
  26. if (!space_group.IT_number.isDefined) return groupName;
  27. if (!space_group['name_H-M_full'].isDefined) return groupNumber;
  28. return groupNumber;
  29. }
  30. function getSymmetry(db: CifCore_Database): Symmetry {
  31. const { cell, space_group } = db;
  32. const nameOrNumber = getSpacegroupNameOrNumber(space_group);
  33. const spaceCell = SpacegroupCell.create(nameOrNumber,
  34. Vec3.create(cell.length_a.value(0), cell.length_b.value(0), cell.length_c.value(0)),
  35. Vec3.scale(Vec3.zero(), Vec3.create(cell.angle_alpha.value(0), cell.angle_beta.value(0), cell.angle_gamma.value(0)), Math.PI / 180));
  36. return {
  37. spacegroup: Spacegroup.create(spaceCell),
  38. assemblies : [],
  39. isNonStandardCrystalFrame: false,
  40. ncsOperators: []
  41. };
  42. }
  43. async function getModels(db: CifCore_Database, format: CifCoreFormat, ctx: RuntimeContext): Promise<Model[]> {
  44. const atomCount = db.atom_site._rowCount;
  45. const MOL = Column.ofConst('MOL', atomCount, Column.Schema.str);
  46. const A = Column.ofConst('A', atomCount, Column.Schema.str);
  47. const seq_id = Column.ofConst(1, atomCount, Column.Schema.int);
  48. const symmetry = getSymmetry(db);
  49. const m = symmetry.spacegroup.cell.fromFractional;
  50. const { fract_x, fract_y, fract_z } = db.atom_site;
  51. const x = new Float32Array(atomCount);
  52. const y = new Float32Array(atomCount);
  53. const z = new Float32Array(atomCount);
  54. const v = Vec3();
  55. for (let i = 0; i < atomCount; ++i) {
  56. Vec3.set(v, fract_x.value(i), fract_y.value(i), fract_z.value(i));
  57. Vec3.transformMat4(v, v, m);
  58. x[i] = v[0], y[i] = v[1], z[i] = v[2];
  59. }
  60. const { type_symbol, label } = db.atom_site;
  61. let typeSymbol: Column<string>;
  62. let formalCharge: Column<number>;
  63. if (type_symbol.isDefined) {
  64. const element_symbol = new Array<string>(atomCount);
  65. const formal_charge = new Int8Array(atomCount);
  66. for (let i = 0; i < atomCount; ++i) {
  67. const ts = type_symbol.value(i);
  68. const n = ts.length;
  69. if (ts[n - 1] === '+') {
  70. element_symbol[i] = ts.substring(0, n - 2);
  71. formal_charge[i] = parseInt(ts[n - 2]);
  72. } else if (ts[n - 2] === '+') {
  73. element_symbol[i] = ts.substring(0, n - 2);
  74. formal_charge[i] = parseInt(ts[n - 1]);
  75. } else if (ts[n - 1] === '-') {
  76. element_symbol[i] = ts.substring(0, n - 2);
  77. formal_charge[i] = -parseInt(ts[n - 2]);
  78. } else if (ts[n - 2] === '-') {
  79. element_symbol[i] = ts.substring(0, n - 2);
  80. formal_charge[i] = -parseInt(ts[n - 1]);
  81. } else {
  82. element_symbol[i] = ts;
  83. formal_charge[i] = 0;
  84. }
  85. }
  86. typeSymbol = Column.ofStringArray(element_symbol);
  87. formalCharge = Column.ofIntArray(formal_charge);
  88. } else {
  89. const element_symbol = new Array<string>(atomCount);
  90. for (let i = 0; i < atomCount; ++i) {
  91. // TODO can take as is if type_symbol not given?
  92. element_symbol[i] = guessElementSymbolString(label.value(i));
  93. }
  94. typeSymbol = Column.ofStringArray(element_symbol);
  95. formalCharge = Column.Undefined(atomCount, Column.Schema.int);
  96. }
  97. const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, {
  98. auth_asym_id: A,
  99. auth_atom_id: label,
  100. auth_comp_id: MOL,
  101. auth_seq_id: seq_id,
  102. Cartn_x: Column.ofFloatArray(x),
  103. Cartn_y: Column.ofFloatArray(y),
  104. Cartn_z: Column.ofFloatArray(z),
  105. id: Column.range(0, atomCount - 1),
  106. label_asym_id: A,
  107. label_atom_id: label,
  108. label_comp_id: MOL,
  109. label_seq_id: seq_id,
  110. label_entity_id: Column.ofConst('1', atomCount, Column.Schema.str),
  111. occupancy: db.atom_site.occupancy.isDefined
  112. ? db.atom_site.occupancy
  113. : Column.ofConst(1, atomCount, Column.Schema.float),
  114. type_symbol: typeSymbol,
  115. pdbx_formal_charge: formalCharge,
  116. pdbx_PDB_model_num: Column.ofConst(1, atomCount, Column.Schema.int),
  117. B_iso_or_equiv: db.atom_site.U_iso_or_equiv,
  118. }, atomCount);
  119. const name = (
  120. db.chemical.name_common.value(0) ||
  121. db.chemical.name_systematic.value(0) ||
  122. db.chemical_formula.sum.value(0)
  123. );
  124. const entityBuilder = new EntityBuilder();
  125. entityBuilder.setNames([['MOL', name || 'Unknown Entity']]);
  126. entityBuilder.getEntityId('MOL', MoleculeType.Unknown, 'A');
  127. const componentBuilder = new ComponentBuilder(seq_id, db.atom_site.type_symbol);
  128. componentBuilder.setNames([['MOL', name || 'Unknown Molecule']]);
  129. componentBuilder.add('MOL', 0);
  130. const basics = createBasic({
  131. entity: entityBuilder.getEntityTable(),
  132. chem_comp: componentBuilder.getChemCompTable(),
  133. atom_site
  134. });
  135. const models = await createModels(basics, format, ctx);
  136. if (models.length > 0) {
  137. ModelSymmetry.Provider.set(models[0], symmetry);
  138. const bondCount = db.geom_bond._rowCount;
  139. if(bondCount > 0) {
  140. const labelIndexMap: { [label: string]: number } = {};
  141. const { label } = db.atom_site;
  142. for (let i = 0, il = label.rowCount; i < il; ++i) {
  143. labelIndexMap[label.value(i)] = i;
  144. }
  145. const indexA: number[] = [];
  146. const indexB: number[] = [];
  147. const order: number[] = [];
  148. const symmetryA: string[] = [];
  149. const symmetryB: string[] = [];
  150. const { atom_site_label_1, atom_site_label_2, valence, site_symmetry_1, site_symmetry_2 } = db.geom_bond;
  151. for (let i = 0; i < bondCount; ++i) {
  152. indexA[i] = labelIndexMap[atom_site_label_1.value(i)];
  153. indexB[i] = labelIndexMap[atom_site_label_2.value(i)];
  154. // TODO derive order from bond length if undefined
  155. order[i] = valence.isDefined ? valence.value(i) : 1;
  156. symmetryA[i] = site_symmetry_1.value(i) || '1_555';
  157. symmetryB[i] = site_symmetry_2.value(i) || '1_555';
  158. }
  159. IndexPairBonds.Provider.set(models[0], IndexPairBonds.fromData({ pairs: {
  160. indexA: Column.ofIntArray(indexA),
  161. indexB: Column.ofIntArray(indexB),
  162. order: Column.ofIntArray(order),
  163. symmetryA: Column.ofStringArray(symmetryA),
  164. symmetryB: Column.ofStringArray(symmetryB)
  165. }, count: indexA.length }));
  166. }
  167. }
  168. return models;
  169. }
  170. function atomSiteAnisotropFromCifCore(model: Model) {
  171. if (!CifCoreFormat.is(model.sourceData)) return;
  172. const { atom_site, atom_site_aniso } = model.sourceData.data.db;
  173. const data = Table.ofPartialColumns(AtomSiteAnisotrop.Schema, {
  174. U: atom_site_aniso.U,
  175. U_esd: atom_site_aniso.U_su
  176. }, atom_site_aniso._rowCount);
  177. const elementToAnsiotrop = AtomSiteAnisotrop.getElementToAnsiotropFromLabel(atom_site.label, atom_site_aniso.label);
  178. return { data, elementToAnsiotrop };
  179. }
  180. function atomSiteAnisotropApplicableCifCore(model: Model) {
  181. if (!CifCoreFormat.is(model.sourceData)) return false;
  182. return model.sourceData.data.db.atom_site_aniso.U.isDefined;
  183. }
  184. AtomSiteAnisotrop.Provider.formatRegistry.add('cifCore', atomSiteAnisotropFromCifCore, atomSiteAnisotropApplicableCifCore);
  185. //
  186. export { CifCoreFormat };
  187. type CifCoreFormat = ModelFormat<CifCoreFormat.Data>
  188. namespace CifCoreFormat {
  189. export type Data = { db: CifCore_Database, frame: CifFrame }
  190. export function is(x: ModelFormat): x is CifCoreFormat {
  191. return x.kind === 'cifCore';
  192. }
  193. export function fromFrame(frame: CifFrame, db?: CifCore_Database): CifCoreFormat {
  194. if (!db) db = CIF.schema.cifCore(frame);
  195. const name = (
  196. db.database_code.depnum_ccdc_archive.value(0) ||
  197. db.database_code.depnum_ccdc_fiz.value(0) ||
  198. db.database_code.ICSD.value(0) ||
  199. db.database_code.MDF.value(0) ||
  200. db.database_code.NBS.value(0) ||
  201. db.database_code.CSD.value(0) ||
  202. db.database_code.COD.value(0) ||
  203. db._name
  204. );
  205. return { kind: 'cifCore', name, data: { db, frame } };
  206. }
  207. }
  208. export function trajectoryFromCifCore(frame: CifFrame): Task<Model.Trajectory> {
  209. const format = CifCoreFormat.fromFrame(frame);
  210. return Task.create('Parse CIF Core', ctx => getModels(format.data.db, format, ctx));
  211. }