structure-selection-query.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. /**
  2. * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. import { CustomProperty } from '../../mol-model-props/common/custom-property';
  8. import { QueryContext, Structure, StructureQuery, StructureSelection, StructureProperties, StructureElement } from '../../mol-model/structure';
  9. import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType, AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames, ElementSymbol } from '../../mol-model/structure/model/types';
  10. import { PluginContext } from '../../mol-plugin/context';
  11. import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
  12. import Expression from '../../mol-script/language/expression';
  13. import { compile } from '../../mol-script/runtime/query/compiler';
  14. import { StateBuilder } from '../../mol-state';
  15. import { RuntimeContext } from '../../mol-task';
  16. import { SetUtils } from '../../mol-util/set';
  17. import { PluginStateObject } from '../objects';
  18. import { StateTransforms } from '../transforms';
  19. import { ElementNames } from '../../mol-model/structure/model/properties/atomic/types';
  20. export enum StructureSelectionCategory {
  21. Type = 'Type',
  22. Structure = 'Structure Property',
  23. Atom = 'Atom Property',
  24. Bond = 'Bond Property',
  25. Residue = 'Residue Property',
  26. AminoAcid = 'Amino Acid',
  27. NucleicBase = 'Nucleic Base',
  28. Manipulate = 'Manipulate Selection',
  29. Validation = 'Validation',
  30. Misc = 'Miscellaneous',
  31. Internal = 'Internal',
  32. }
  33. export { StructureSelectionQuery };
  34. interface StructureSelectionQuery {
  35. readonly label: string
  36. readonly expression: Expression
  37. readonly description: string
  38. readonly category: string
  39. readonly isHidden: boolean
  40. readonly priority: number
  41. readonly referencesCurrent: boolean
  42. readonly query: StructureQuery
  43. readonly ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
  44. getSelection(plugin: PluginContext, runtime: RuntimeContext, structure: Structure): Promise<StructureSelection>
  45. }
  46. interface StructureSelectionQueryProps {
  47. description?: string
  48. category?: string
  49. isHidden?: boolean
  50. priority?: number
  51. referencesCurrent?: boolean
  52. ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
  53. }
  54. function StructureSelectionQuery(label: string, expression: Expression, props: StructureSelectionQueryProps = {}): StructureSelectionQuery {
  55. let _query: StructureQuery;
  56. return {
  57. label,
  58. expression,
  59. description: props.description || '',
  60. category: props.category ?? StructureSelectionCategory.Misc,
  61. isHidden: !!props.isHidden,
  62. priority: props.priority || 0,
  63. referencesCurrent: !!props.referencesCurrent,
  64. get query() {
  65. if (!_query) _query = compile<StructureSelection>(expression);
  66. return _query;
  67. },
  68. ensureCustomProperties: props.ensureCustomProperties,
  69. async getSelection(plugin, runtime, structure) {
  70. const current = plugin.managers.structure.selection.getStructure(structure);
  71. const currentSelection = current ? StructureSelection.Sequence(structure, [current]) : StructureSelection.Empty(structure);
  72. if (props.ensureCustomProperties) {
  73. await props.ensureCustomProperties({ runtime, assetManager: plugin.managers.asset }, structure);
  74. }
  75. if (!_query) _query = compile<StructureSelection>(expression);
  76. return _query(new QueryContext(structure, { currentSelection }));
  77. }
  78. };
  79. }
  80. const all = StructureSelectionQuery('All', MS.struct.generator.all(), { category: '', priority: 1000 });
  81. const current = StructureSelectionQuery('Current Selection', MS.internal.generator.current(), { category: '', referencesCurrent: true });
  82. const polymer = StructureSelectionQuery('Polymer', MS.struct.modifier.union([
  83. MS.struct.generator.atomGroups({
  84. 'entity-test': MS.core.logic.and([
  85. MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
  86. MS.core.str.match([
  87. MS.re('(polypeptide|cyclic-pseudo-peptide|nucleotide|peptide nucleic acid)', 'i'),
  88. MS.ammp('entitySubtype')
  89. ])
  90. ])
  91. })
  92. ]), { category: StructureSelectionCategory.Type });
  93. const trace = StructureSelectionQuery('Trace', MS.struct.modifier.union([
  94. MS.struct.combinator.merge([
  95. MS.struct.modifier.union([
  96. MS.struct.generator.atomGroups({
  97. 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
  98. 'chain-test': MS.core.set.has([
  99. MS.set('sphere', 'gaussian'), MS.ammp('objectPrimitive')
  100. ])
  101. })
  102. ]),
  103. MS.struct.modifier.union([
  104. MS.struct.generator.atomGroups({
  105. 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
  106. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  107. 'atom-test': MS.core.set.has([MS.set('CA', 'P'), MS.ammp('label_atom_id')])
  108. })
  109. ])
  110. ])
  111. ]), { category: StructureSelectionCategory.Structure });
  112. const _proteinEntityTest = MS.core.logic.and([
  113. MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
  114. MS.core.str.match([
  115. MS.re('(polypeptide|cyclic-pseudo-peptide)', 'i'),
  116. MS.ammp('entitySubtype')
  117. ])
  118. ]);
  119. const _nucleiEntityTest = MS.core.logic.and([
  120. MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
  121. MS.core.str.match([
  122. MS.re('(nucleotide|peptide nucleic acid)', 'i'),
  123. MS.ammp('entitySubtype')
  124. ])
  125. ]);
  126. // TODO maybe pre-calculate backbone atom properties
  127. const backbone = StructureSelectionQuery('Backbone', MS.struct.modifier.union([
  128. MS.struct.combinator.merge([
  129. MS.struct.modifier.union([
  130. MS.struct.generator.atomGroups({
  131. 'entity-test': _proteinEntityTest,
  132. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  133. 'atom-test': MS.core.set.has([MS.set(...SetUtils.toArray(ProteinBackboneAtoms)), MS.ammp('label_atom_id')])
  134. })
  135. ]),
  136. MS.struct.modifier.union([
  137. MS.struct.generator.atomGroups({
  138. 'entity-test': _nucleiEntityTest,
  139. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  140. 'atom-test': MS.core.set.has([MS.set(...SetUtils.toArray(NucleicBackboneAtoms)), MS.ammp('label_atom_id')])
  141. })
  142. ])
  143. ])
  144. ]), { category: StructureSelectionCategory.Structure });
  145. // TODO maybe pre-calculate sidechain atom property
  146. const sidechain = StructureSelectionQuery('Sidechain', MS.struct.modifier.union([
  147. MS.struct.combinator.merge([
  148. MS.struct.modifier.union([
  149. MS.struct.generator.atomGroups({
  150. 'entity-test': _proteinEntityTest,
  151. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  152. 'atom-test': MS.core.logic.or([
  153. MS.core.logic.not([
  154. MS.core.set.has([MS.set(...SetUtils.toArray(ProteinBackboneAtoms)), MS.ammp('label_atom_id')])
  155. ])
  156. ])
  157. })
  158. ]),
  159. MS.struct.modifier.union([
  160. MS.struct.generator.atomGroups({
  161. 'entity-test': _nucleiEntityTest,
  162. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  163. 'atom-test': MS.core.logic.or([
  164. MS.core.logic.not([
  165. MS.core.set.has([MS.set(...SetUtils.toArray(NucleicBackboneAtoms)), MS.ammp('label_atom_id')])
  166. ])
  167. ])
  168. })
  169. ])
  170. ])
  171. ]), { category: StructureSelectionCategory.Structure });
  172. // TODO maybe pre-calculate sidechain atom property
  173. const sidechainWithTrace = StructureSelectionQuery('Sidechain with Trace', MS.struct.modifier.union([
  174. MS.struct.combinator.merge([
  175. MS.struct.modifier.union([
  176. MS.struct.generator.atomGroups({
  177. 'entity-test': _proteinEntityTest,
  178. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  179. 'atom-test': MS.core.logic.or([
  180. MS.core.logic.not([
  181. MS.core.set.has([MS.set(...SetUtils.toArray(ProteinBackboneAtoms)), MS.ammp('label_atom_id')])
  182. ]),
  183. MS.core.rel.eq([MS.ammp('label_atom_id'), 'CA']),
  184. MS.core.logic.and([
  185. MS.core.rel.eq([MS.ammp('auth_comp_id'), 'PRO']),
  186. MS.core.rel.eq([MS.ammp('label_atom_id'), 'N'])
  187. ])
  188. ])
  189. })
  190. ]),
  191. MS.struct.modifier.union([
  192. MS.struct.generator.atomGroups({
  193. 'entity-test': _nucleiEntityTest,
  194. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  195. 'atom-test': MS.core.logic.or([
  196. MS.core.logic.not([
  197. MS.core.set.has([MS.set(...SetUtils.toArray(NucleicBackboneAtoms)), MS.ammp('label_atom_id')])
  198. ]),
  199. MS.core.rel.eq([MS.ammp('label_atom_id'), 'P'])
  200. ])
  201. })
  202. ])
  203. ])
  204. ]), { category: StructureSelectionCategory.Structure });
  205. const protein = StructureSelectionQuery('Protein', MS.struct.modifier.union([
  206. MS.struct.generator.atomGroups({ 'entity-test': _proteinEntityTest })
  207. ]), { category: StructureSelectionCategory.Type });
  208. const nucleic = StructureSelectionQuery('Nucleic', MS.struct.modifier.union([
  209. MS.struct.generator.atomGroups({ 'entity-test': _nucleiEntityTest })
  210. ]), { category: StructureSelectionCategory.Type });
  211. const helix = StructureSelectionQuery('Helix', MS.struct.modifier.union([
  212. MS.struct.generator.atomGroups({
  213. 'entity-test': _proteinEntityTest,
  214. 'residue-test': MS.core.flags.hasAny([
  215. MS.ammp('secondaryStructureFlags'),
  216. MS.core.type.bitflags([SecondaryStructureType.Flag.Helix])
  217. ])
  218. })
  219. ]), { category: StructureSelectionCategory.Structure });
  220. const beta = StructureSelectionQuery('Beta Strand/Sheet', MS.struct.modifier.union([
  221. MS.struct.generator.atomGroups({
  222. 'entity-test': _proteinEntityTest,
  223. 'residue-test': MS.core.flags.hasAny([
  224. MS.ammp('secondaryStructureFlags'),
  225. MS.core.type.bitflags([SecondaryStructureType.Flag.Beta])
  226. ])
  227. })
  228. ]), { category: StructureSelectionCategory.Structure });
  229. const water = StructureSelectionQuery('Water', MS.struct.modifier.union([
  230. MS.struct.generator.atomGroups({
  231. 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'water'])
  232. })
  233. ]), { category: StructureSelectionCategory.Type });
  234. const branched = StructureSelectionQuery('Carbohydrate', MS.struct.modifier.union([
  235. MS.struct.generator.atomGroups({
  236. 'entity-test': MS.core.logic.or([
  237. MS.core.rel.eq([MS.ammp('entityType'), 'branched']),
  238. MS.core.logic.and([
  239. MS.core.rel.eq([MS.ammp('entityType'), 'non-polymer']),
  240. MS.core.str.match([
  241. MS.re('oligosaccharide', 'i'),
  242. MS.ammp('entitySubtype')
  243. ])
  244. ])
  245. ])
  246. })
  247. ]), { category: StructureSelectionCategory.Type });
  248. const branchedPlusConnected = StructureSelectionQuery('Carbohydrate with Connected', MS.struct.modifier.union([
  249. MS.struct.modifier.includeConnected({
  250. 0: branched.expression, 'layer-count': 1, 'as-whole-residues': true
  251. })
  252. ]), { category: StructureSelectionCategory.Internal, isHidden: true });
  253. const branchedConnectedOnly = StructureSelectionQuery('Connected to Carbohydrate', MS.struct.modifier.union([
  254. MS.struct.modifier.exceptBy({
  255. 0: branchedPlusConnected.expression,
  256. by: branched.expression
  257. })
  258. ]), { category: StructureSelectionCategory.Internal, isHidden: true });
  259. const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
  260. MS.struct.combinator.merge([
  261. MS.struct.modifier.union([
  262. MS.struct.generator.atomGroups({
  263. 'entity-test': MS.core.logic.and([
  264. MS.core.rel.eq([MS.ammp('entityType'), 'non-polymer']),
  265. MS.core.logic.not([MS.core.str.match([
  266. MS.re('oligosaccharide', 'i'),
  267. MS.ammp('entitySubtype')
  268. ])])
  269. ]),
  270. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  271. 'residue-test': MS.core.logic.not([
  272. MS.core.str.match([MS.re('saccharide', 'i'), MS.ammp('chemCompType')])
  273. ])
  274. })
  275. ]),
  276. // this is to get non-polymer and peptide terminus components in polymer entities,
  277. // - non-polymer, e.g. PXZ in 4HIV or generally ACE
  278. // - carboxy terminus, e.g. FC0 in 4BP9, or ETA in 6DDE
  279. // - amino terminus, e.g. ARF in 3K4V, or 4MM in 3EGV
  280. MS.struct.modifier.union([
  281. MS.struct.generator.atomGroups({
  282. 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
  283. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  284. 'residue-test': MS.core.str.match([
  285. MS.re('non-polymer|(amino|carboxy) terminus|peptide-like', 'i'),
  286. MS.ammp('chemCompType')
  287. ])
  288. })
  289. ])
  290. ]),
  291. ]), { category: StructureSelectionCategory.Type });
  292. // don't include branched entities as they have their own link representation
  293. const ligandPlusConnected = StructureSelectionQuery('Ligand with Connected', MS.struct.modifier.union([
  294. MS.struct.modifier.exceptBy({
  295. 0: MS.struct.modifier.union([
  296. MS.struct.modifier.includeConnected({
  297. 0: ligand.expression,
  298. 'layer-count': 1,
  299. 'as-whole-residues': true,
  300. 'bond-test': MS.core.flags.hasAny([
  301. MS.struct.bondProperty.flags(),
  302. MS.core.type.bitflags([
  303. BondType.Flag.Covalent | BondType.Flag.MetallicCoordination
  304. ])
  305. ])
  306. })
  307. ]),
  308. by: branched.expression
  309. })
  310. ]), { category: StructureSelectionCategory.Internal, isHidden: true });
  311. const ligandConnectedOnly = StructureSelectionQuery('Connected to Ligand', MS.struct.modifier.union([
  312. MS.struct.modifier.exceptBy({
  313. 0: ligandPlusConnected.expression,
  314. by: ligand.expression
  315. })
  316. ]), { category: StructureSelectionCategory.Internal, isHidden: true });
  317. // residues connected to ligands or branched entities
  318. const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydrate', MS.struct.modifier.union([
  319. MS.struct.combinator.merge([
  320. branchedConnectedOnly.expression,
  321. ligandConnectedOnly.expression
  322. ]),
  323. ]), { category: StructureSelectionCategory.Internal, isHidden: true });
  324. const disulfideBridges = StructureSelectionQuery('Disulfide Bridges', MS.struct.modifier.union([
  325. MS.struct.modifier.wholeResidues([
  326. MS.struct.modifier.union([
  327. MS.struct.generator.bondedAtomicPairs({
  328. 0: MS.core.flags.hasAny([
  329. MS.struct.bondProperty.flags(),
  330. MS.core.type.bitflags([BondType.Flag.Disulfide])
  331. ])
  332. })
  333. ])
  334. ])
  335. ]), { category: StructureSelectionCategory.Bond });
  336. const nonStandardPolymer = StructureSelectionQuery('Non-standard Residues in Polymers', MS.struct.modifier.union([
  337. MS.struct.generator.atomGroups({
  338. 'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer']),
  339. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  340. 'residue-test': MS.ammp('isNonStandard')
  341. })
  342. ]), { category: StructureSelectionCategory.Residue });
  343. const coarse = StructureSelectionQuery('Coarse Elements', MS.struct.modifier.union([
  344. MS.struct.generator.atomGroups({
  345. 'chain-test': MS.core.set.has([
  346. MS.set('sphere', 'gaussian'), MS.ammp('objectPrimitive')
  347. ])
  348. })
  349. ]), { category: StructureSelectionCategory.Type });
  350. const ring = StructureSelectionQuery('Rings in Residues', MS.struct.modifier.union([
  351. MS.struct.generator.rings()
  352. ]), { category: StructureSelectionCategory.Residue });
  353. const aromaticRing = StructureSelectionQuery('Aromatic Rings in Residues', MS.struct.modifier.union([
  354. MS.struct.generator.rings({ 'only-aromatic': true })
  355. ]), { category: StructureSelectionCategory.Residue });
  356. const surroundings = StructureSelectionQuery('Surrounding Residues (5 \u212B) of Selection', MS.struct.modifier.union([
  357. MS.struct.modifier.exceptBy({
  358. 0: MS.struct.modifier.includeSurroundings({
  359. 0: MS.internal.generator.current(),
  360. radius: 5,
  361. 'as-whole-residues': true
  362. }),
  363. by: MS.internal.generator.current()
  364. })
  365. ]), {
  366. description: 'Select residues within 5 \u212B of the current selection.',
  367. category: StructureSelectionCategory.Manipulate,
  368. referencesCurrent: true
  369. });
  370. const complement = StructureSelectionQuery('Inverse / Complement of Selection', MS.struct.modifier.union([
  371. MS.struct.modifier.exceptBy({
  372. 0: MS.struct.generator.all(),
  373. by: MS.internal.generator.current()
  374. })
  375. ]), {
  376. description: 'Select everything not in the current selection.',
  377. category: StructureSelectionCategory.Manipulate,
  378. referencesCurrent: true
  379. });
  380. const covalentlyBonded = StructureSelectionQuery('Residues Covalently Bonded to Selection', MS.struct.modifier.union([
  381. MS.struct.modifier.includeConnected({
  382. 0: MS.internal.generator.current(), 'layer-count': 1, 'as-whole-residues': true
  383. })
  384. ]), {
  385. description: 'Select residues covalently bonded to current selection.',
  386. category: StructureSelectionCategory.Manipulate,
  387. referencesCurrent: true
  388. });
  389. const covalentlyOrMetallicBonded = StructureSelectionQuery('Residues with Cov. or Metallic Bond to Selection', MS.struct.modifier.union([
  390. MS.struct.modifier.includeConnected({
  391. 0: MS.internal.generator.current(),
  392. 'layer-count': 1,
  393. 'as-whole-residues': true,
  394. 'bond-test': MS.core.flags.hasAny([
  395. MS.struct.bondProperty.flags(),
  396. MS.core.type.bitflags([
  397. BondType.Flag.Covalent | BondType.Flag.MetallicCoordination
  398. ])
  399. ])
  400. })
  401. ]), {
  402. description: 'Select residues covalently bonded to current selection.',
  403. category: StructureSelectionCategory.Manipulate,
  404. referencesCurrent: true
  405. });
  406. const wholeResidues = StructureSelectionQuery('Whole Residues of Selection', MS.struct.modifier.union([
  407. MS.struct.modifier.wholeResidues({
  408. 0: MS.internal.generator.current()
  409. })
  410. ]), {
  411. description: 'Expand current selection to whole residues.',
  412. category: StructureSelectionCategory.Manipulate,
  413. referencesCurrent: true
  414. });
  415. const StandardAminoAcids = [
  416. [['HIS'], 'Histidine'],
  417. [['ARG'], 'Arginine'],
  418. [['LYS'], 'Lysine'],
  419. [['ILE'], 'Isoleucine'],
  420. [['PHE'], 'Phenylalanine'],
  421. [['LEU'], 'Leucine'],
  422. [['TRP'], 'Tryptophan'],
  423. [['ALA'], 'Alanine'],
  424. [['MET'], 'Methionine'],
  425. [['PRO'], 'Proline'],
  426. [['CYS'], 'Cysteine'],
  427. [['ASN'], 'Asparagine'],
  428. [['VAL'], 'Valine'],
  429. [['GLY'], 'Glycine'],
  430. [['SER'], 'Serine'],
  431. [['GLN'], 'Glutamine'],
  432. [['TYR'], 'Tyrosine'],
  433. [['ASP'], 'Aspartic Acid'],
  434. [['GLU'], 'Glutamic Acid'],
  435. [['THR'], 'Threonine'],
  436. [['SEC'], 'Selenocysteine'],
  437. [['PYL'], 'Pyrrolysine'],
  438. [['UNK'], 'Unknown'],
  439. ].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
  440. const StandardNucleicBases = [
  441. [['A', 'DA'], 'Adenosine'],
  442. [['C', 'DC'], 'Cytidine'],
  443. [['T', 'DT'], 'Thymidine'],
  444. [['G', 'DG'], 'Guanosine'],
  445. [['I', 'DI'], 'Inosine'],
  446. [['U', 'DU'], 'Uridine'],
  447. [['N', 'DN'], 'Unknown'],
  448. ].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
  449. export function ResidueQuery([names, label]: [string[], string], category: string, priority = 0) {
  450. const description = names.length === 1 && !StandardResidues.has(names[0])
  451. ? `[${names[0]}] ${label}`
  452. : `${label} (${names.join(', ')})`;
  453. return StructureSelectionQuery(description, MS.struct.modifier.union([
  454. MS.struct.generator.atomGroups({
  455. 'residue-test': MS.core.set.has([MS.set(...names), MS.ammp('auth_comp_id')])
  456. })
  457. ]), { category, priority, description });
  458. }
  459. export function ElementSymbolQuery([names, label]: [string[], string], category: string, priority: number) {
  460. const description = `${label} (${names.join(', ')})`;
  461. return StructureSelectionQuery(description, MS.struct.modifier.union([
  462. MS.struct.generator.atomGroups({
  463. 'atom-test': MS.core.set.has([MS.set(...names), MS.acp('elementSymbol')])
  464. })
  465. ]), { category, priority, description });
  466. }
  467. export function EntityDescriptionQuery([names, label]: [string[], string], category: string, priority: number) {
  468. const description = `${label}`;
  469. return StructureSelectionQuery(`${label}`, MS.struct.modifier.union([
  470. MS.struct.generator.atomGroups({
  471. 'entity-test': MS.core.list.equal([MS.list(...names), MS.ammp('entityDescription')])
  472. })
  473. ]), { category, priority, description });
  474. }
  475. const StandardResidues = SetUtils.unionMany(
  476. AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames
  477. );
  478. export function getElementQueries(structures: Structure[]) {
  479. const uniqueElements = new Set<ElementSymbol>();
  480. for (const structure of structures) {
  481. structure.uniqueElementSymbols.forEach(e => uniqueElements.add(e));
  482. }
  483. const queries: StructureSelectionQuery[] = [];
  484. uniqueElements.forEach(e => {
  485. const label = ElementNames[e] || e;
  486. queries.push(ElementSymbolQuery([[e], label], 'Element Symbol', 0));
  487. });
  488. return queries;
  489. }
  490. export function getNonStandardResidueQueries(structures: Structure[]) {
  491. const residueLabels = new Map<string, string>();
  492. const uniqueResidues = new Set<string>();
  493. for (const structure of structures) {
  494. structure.uniqueResidueNames.forEach(r => uniqueResidues.add(r));
  495. for (const m of structure.models) {
  496. structure.uniqueResidueNames.forEach(r => {
  497. const comp = m.properties.chemicalComponentMap.get(r);
  498. if (comp) residueLabels.set(r, comp.name);
  499. });
  500. }
  501. }
  502. const queries: StructureSelectionQuery[] = [];
  503. SetUtils.difference(uniqueResidues, StandardResidues).forEach(r => {
  504. const label = residueLabels.get(r) || r;
  505. queries.push(ResidueQuery([[r], label], 'Ligand/Non-standard Residue', 200));
  506. });
  507. return queries;
  508. }
  509. export function getPolymerAndBranchedEntityQueries(structures: Structure[]) {
  510. const uniqueEntities = new Map<string, string[]>();
  511. const l = StructureElement.Location.create();
  512. for (const structure of structures) {
  513. l.structure = structure;
  514. for (const ug of structure.unitSymmetryGroups) {
  515. l.unit = ug.units[0];
  516. l.element = ug.elements[0];
  517. const entityType = StructureProperties.entity.type(l);
  518. if (entityType === 'polymer' || entityType === 'branched') {
  519. const description = StructureProperties.entity.pdbx_description(l);
  520. uniqueEntities.set(description.join(', '), description);
  521. }
  522. }
  523. }
  524. const queries: StructureSelectionQuery[] = [];
  525. uniqueEntities.forEach((v, k) => {
  526. queries.push(EntityDescriptionQuery([v, k], 'Polymer/Carbohydrate Entities', 300));
  527. });
  528. return queries;
  529. }
  530. export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
  531. return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
  532. { expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
  533. { tags: customTag ? [query, customTag] : [query] });
  534. }
  535. export const StructureSelectionQueries = {
  536. all,
  537. current,
  538. polymer,
  539. trace,
  540. backbone,
  541. sidechain,
  542. sidechainWithTrace,
  543. protein,
  544. nucleic,
  545. helix,
  546. beta,
  547. water,
  548. branched,
  549. branchedPlusConnected,
  550. branchedConnectedOnly,
  551. ligand,
  552. ligandPlusConnected,
  553. ligandConnectedOnly,
  554. connectedOnly,
  555. disulfideBridges,
  556. nonStandardPolymer,
  557. coarse,
  558. ring,
  559. aromaticRing,
  560. surroundings,
  561. complement,
  562. covalentlyBonded,
  563. covalentlyOrMetallicBonded,
  564. wholeResidues,
  565. };
  566. export class StructureSelectionQueryRegistry {
  567. list: StructureSelectionQuery[] = []
  568. options: [StructureSelectionQuery, string, string][] = []
  569. version = 1
  570. add(q: StructureSelectionQuery) {
  571. this.list.push(q);
  572. this.options.push([q, q.label, q.category]);
  573. this.version += 1;
  574. }
  575. remove(q: StructureSelectionQuery) {
  576. const idx = this.list.indexOf(q);
  577. if (idx !== -1) {
  578. this.list.splice(idx, 1);
  579. this.options.splice(idx, 1);
  580. this.version += 1;
  581. }
  582. }
  583. constructor() {
  584. // add built-in
  585. this.list.push(
  586. ...Object.values(StructureSelectionQueries),
  587. ...StandardAminoAcids.map(v => ResidueQuery(v, StructureSelectionCategory.AminoAcid)),
  588. ...StandardNucleicBases.map(v => ResidueQuery(v, StructureSelectionCategory.NucleicBase))
  589. );
  590. this.options.push(...this.list.map(q => [q, q.label, q.category] as [StructureSelectionQuery, string, string]));
  591. }
  592. }