operators.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /**
  2. * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
  6. */
  7. import * as P from '../../../mol-util/monadic-parser';
  8. import * as h from '../helper';
  9. import { MolScriptBuilder } from '../../../mol-script/language/builder';
  10. const B = MolScriptBuilder;
  11. import { OperatorList } from '../types';
  12. import { Expression } from '../../language/expression';
  13. export const operators: OperatorList = [
  14. {
  15. '@desc': 'Selects atoms that are not included in s1.',
  16. '@examples': [
  17. 'NOT resn ALA',
  18. 'not (resi 42 or chain A)',
  19. '!resi 42 or chain A',
  20. ],
  21. name: 'not',
  22. type: h.prefix,
  23. rule: P.MonadicParser.alt(
  24. P.MonadicParser.regexp(/NOT/i).skip(P.MonadicParser.whitespace),
  25. P.MonadicParser.string('!').skip(P.MonadicParser.optWhitespace)
  26. ),
  27. map: (op, selection) => h.invertExpr(selection),
  28. },
  29. {
  30. '@desc': 'Selects atoms included in both s1 and s2.',
  31. '@examples': ['chain A AND name CA'],
  32. name: 'and',
  33. type: h.binaryLeft,
  34. rule: h.infixOp(/AND|&/i),
  35. map: (op, selection, by) =>
  36. B.struct.modifier.intersectBy({ 0: selection, by }),
  37. },
  38. {
  39. '@desc': 'Selects atoms included in either s1 or s2.',
  40. '@examples': ['chain A OR chain B'],
  41. name: 'or',
  42. type: h.binaryLeft,
  43. rule: h.infixOp(/OR|\|/i),
  44. map: (op: string, s1: Expression, s2: Expression) => B.struct.combinator.merge([s1, s2]),
  45. },
  46. {
  47. '@desc':
  48. 'Selects atoms in s1 whose identifiers name, resi, resn, chain and segi all match atoms in s2.',
  49. '@examples': ['chain A IN chain B'],
  50. name: 'in',
  51. type: h.binaryLeft,
  52. rule: h.infixOp(/IN/i),
  53. map: (op: string, selection: Expression, source: Expression) => {
  54. return B.struct.filter.withSameAtomProperties({
  55. 0: selection,
  56. source,
  57. property: B.core.type.compositeKey([
  58. B.ammp('label_atom_id'),
  59. B.ammp('label_seq_id'),
  60. B.ammp('label_comp_id'),
  61. B.ammp('auth_asym_id'),
  62. B.ammp('label_asym_id'),
  63. ]),
  64. });
  65. },
  66. },
  67. {
  68. '@desc':
  69. 'Selects atoms in s1 whose identifiers name and resi match atoms in s2.',
  70. '@examples': ['chain A LIKE chain B'],
  71. name: 'like',
  72. type: h.binaryLeft,
  73. rule: h.infixOp(/LIKE|l\./i),
  74. map: (op: string, selection: Expression, source: Expression) => {
  75. return B.struct.filter.withSameAtomProperties({
  76. 0: selection,
  77. source,
  78. property: B.core.type.compositeKey([
  79. B.ammp('label_atom_id'),
  80. B.ammp('label_seq_id'),
  81. ]),
  82. });
  83. },
  84. },
  85. {
  86. '@desc':
  87. 'Selects all atoms whose van der Waals radii are separated from the van der Waals radii of s1 by a minimum of X Angstroms.',
  88. '@examples': ['solvent GAP 2'],
  89. name: 'gap',
  90. type: h.postfix,
  91. rule: h
  92. .postfixOp(/GAP\s+([-+]?[0-9]*\.?[0-9]+)/i, 1)
  93. .map((x: any) => parseFloat(x)),
  94. map: (distance: number, target: Expression) => {
  95. return B.struct.filter.within({
  96. '0': B.struct.generator.atomGroups(),
  97. target,
  98. 'atom-radius': B.acp('vdw'),
  99. 'max-radius': distance,
  100. invert: true,
  101. });
  102. },
  103. },
  104. {
  105. '@desc':
  106. 'Selects atoms with centers within X Angstroms of the center of any atom in s1.',
  107. '@examples': ['resname LIG AROUND 1'],
  108. name: 'around',
  109. abbr: ['a.'],
  110. type: h.postfix,
  111. rule: h
  112. .postfixOp(/(AROUND|a\.)\s+([-+]?[0-9]*\.?[0-9]+)/i, 2)
  113. .map((x: any) => parseFloat(x)),
  114. map: (radius: number, target: Expression) => {
  115. return B.struct.modifier.exceptBy({
  116. '0': B.struct.filter.within({
  117. '0': B.struct.generator.atomGroups(),
  118. target,
  119. 'max-radius': radius,
  120. }),
  121. by: target,
  122. });
  123. },
  124. },
  125. {
  126. '@desc':
  127. 'Expands s1 by all atoms within X Angstroms of the center of any atom in s1.',
  128. '@examples': ['chain A EXPAND 3'],
  129. name: 'expand',
  130. abbr: ['x.'],
  131. type: h.postfix,
  132. rule: h
  133. .postfixOp(/(EXPAND|x\.)\s+([-+]?[0-9]*\.?[0-9]+)/i, 2)
  134. .map((x: any) => parseFloat(x)),
  135. map: (radius: number, selection: Expression) => {
  136. return B.struct.modifier.includeSurroundings({ 0: selection, radius });
  137. },
  138. },
  139. {
  140. '@desc':
  141. 'Selects atoms in s1 that are within X Angstroms of any atom in s2.',
  142. '@examples': ['chain A WITHIN 3 OF chain B'],
  143. name: 'within',
  144. abbr: ['w.'],
  145. type: h.binaryLeft,
  146. rule: h.ofOp('WITHIN', 'w.'),
  147. map: (radius: number, selection: Expression, target: Expression) => {
  148. return B.struct.filter.within({
  149. 0: selection,
  150. target,
  151. 'max-radius': radius,
  152. });
  153. },
  154. },
  155. {
  156. '@desc':
  157. 'Same as within, but excludes s2 from the selection (and thus is identical to s1 and s2 around X).',
  158. '@examples': ['chain A NEAR_TO 3 OF chain B'],
  159. name: 'near_to',
  160. abbr: ['nto.'],
  161. type: h.binaryLeft,
  162. rule: h.ofOp('NEAR_TO', 'nto.'),
  163. map: (radius: number, selection: Expression, target: Expression) => {
  164. return B.struct.modifier.exceptBy({
  165. '0': B.struct.filter.within({
  166. '0': selection,
  167. target,
  168. 'max-radius': radius,
  169. }),
  170. by: target,
  171. });
  172. },
  173. },
  174. {
  175. '@desc': 'Selects atoms in s1 that are at least X Anstroms away from s2.',
  176. '@examples': ['solvent BEYOND 2 OF chain A'],
  177. name: 'beyond',
  178. abbr: ['be.'],
  179. type: h.binaryLeft,
  180. rule: h.ofOp('BEYOND', 'be.'),
  181. map: (radius: number, selection: Expression, target: Expression) => {
  182. return B.struct.modifier.exceptBy({
  183. '0': B.struct.filter.within({
  184. '0': selection,
  185. target,
  186. 'max-radius': radius,
  187. invert: true,
  188. }),
  189. by: target,
  190. });
  191. },
  192. },
  193. {
  194. '@desc': 'Expands selection to complete residues.',
  195. '@examples': ['BYRESIDUE name N'],
  196. name: 'byresidue',
  197. abbr: ['byresi', 'byres', 'br.'],
  198. type: h.prefix,
  199. rule: h.prefixOp(/BYRESIDUE|byresi|byres|br\./i),
  200. map: (op: string, selection: Expression) => {
  201. return h.asAtoms(
  202. B.struct.modifier.expandProperty({
  203. '0': B.struct.modifier.union({ 0: selection }),
  204. property: B.ammp('residueKey'),
  205. })
  206. );
  207. },
  208. },
  209. {
  210. '@desc':
  211. 'Completely selects all alpha carbons in all residues covered by a selection.',
  212. '@examples': ['BYCALPHA chain A'],
  213. name: 'bycalpha',
  214. abbr: ['bca.'],
  215. type: h.prefix,
  216. rule: h.prefixOp(/BYCALPHA|bca\./i),
  217. map: (op: string, selection: Expression) => {
  218. return B.struct.generator.queryInSelection({
  219. '0': B.struct.modifier.expandProperty({
  220. '0': B.struct.modifier.union({ 0: selection }),
  221. property: B.ammp('residueKey'),
  222. }),
  223. query: B.struct.generator.atomGroups({
  224. 'atom-test': B.core.rel.eq([
  225. B.atomName('CA'),
  226. B.ammp('label_atom_id'),
  227. ]),
  228. }),
  229. });
  230. },
  231. },
  232. {
  233. '@desc': 'Expands selection to complete molecules.',
  234. '@examples': ['BYMOLECULE resi 20-30'],
  235. name: 'bymolecule',
  236. isUnsupported: true, // structure-query.atom-property.topology.connected-component-key' is not implemented
  237. abbr: ['bymol', 'bm.'],
  238. type: h.prefix,
  239. rule: h.prefixOp(/BYMOLECULE|bymol|bm\./i),
  240. map: (op: string, selection: Expression) => {
  241. return h.asAtoms(
  242. B.struct.modifier.expandProperty({
  243. '0': B.struct.modifier.union({ 0: selection }),
  244. property: B.atp('connectedComponentKey'),
  245. })
  246. );
  247. },
  248. },
  249. {
  250. '@desc': 'Expands selection to complete fragments.',
  251. '@examples': ['BYFRAGMENT resi 10'],
  252. name: 'byfragment',
  253. abbr: ['byfrag', 'bf.'],
  254. isUnsupported: true,
  255. type: h.prefix,
  256. rule: h.prefixOp(/BYFRAGMENT|byfrag|bf\./i),
  257. map: (op: string, selection: Expression) => [op, selection],
  258. },
  259. {
  260. '@desc': 'Expands selection to complete segments.',
  261. '@examples': ['BYSEGMENT resn CYS'],
  262. name: 'bysegment',
  263. abbr: ['bysegi', 'byseg', 'bs.'],
  264. type: h.prefix,
  265. rule: h.prefixOp(/BYSEGMENT|bysegi|byseg|bs\./i),
  266. map: (op: string, selection: Expression) => {
  267. return h.asAtoms(
  268. B.struct.modifier.expandProperty({
  269. '0': B.struct.modifier.union({ 0: selection }),
  270. property: B.ammp('chainKey'),
  271. })
  272. );
  273. },
  274. },
  275. {
  276. '@desc': 'Expands selection to complete objects.',
  277. '@examples': ['BYOBJECT chain A'],
  278. name: 'byobject',
  279. abbr: ['byobj', 'bo.'],
  280. isUnsupported: true,
  281. type: h.prefix,
  282. rule: h.prefixOp(/BYOBJECT|byobj|bo\./i),
  283. map: (op: string, selection: Expression) => [op, selection],
  284. },
  285. {
  286. '@desc': 'Expands selection to unit cell.',
  287. '@examples': ['BYCELL chain A'],
  288. name: 'bycell',
  289. isUnsupported: true,
  290. type: h.prefix,
  291. rule: h.prefixOp(/BYCELL/i),
  292. map: (op: string, selection: Expression) => [op, selection],
  293. },
  294. {
  295. '@desc': 'All rings of size ≤ 7 which have at least one atom in s1.',
  296. '@examples': ['BYRING resn HEM'],
  297. name: 'byring',
  298. // isUnsupported: true, // structure-query.atom-set.atom-count' is not implemented.
  299. type: h.prefix,
  300. rule: h.prefixOp(/BYRING/i),
  301. map: (op: string, selection: Expression) => {
  302. return h.asAtoms(
  303. B.struct.modifier.intersectBy({
  304. '0': B.struct.filter.pick({
  305. '0': B.struct.generator.rings(),
  306. test: B.core.logic.and([
  307. B.core.rel.lte([B.struct.atomSet.atomCount(), 7]),
  308. B.core.rel.gr([B.struct.atomSet.countQuery([selection]), 1]),
  309. ]),
  310. }),
  311. by: selection,
  312. })
  313. );
  314. },
  315. },
  316. {
  317. '@desc': 'Selects atoms directly bonded to s1, excludes s1.',
  318. '@examples': ['NEIGHBOR resn CYS'],
  319. name: 'neighbor',
  320. type: h.prefix,
  321. abbr: ['nbr.'],
  322. rule: h.prefixOp(/NEIGHBOR|nbr\./i),
  323. map: (op: string, selection: Expression) => {
  324. return B.struct.modifier.exceptBy({
  325. '0': h.asAtoms(
  326. B.struct.modifier.includeConnected({
  327. '0': B.struct.modifier.union({ 0: selection }),
  328. 'bond-test': true,
  329. })
  330. ),
  331. by: selection,
  332. });
  333. },
  334. },
  335. {
  336. '@desc': 'Selects atoms directly bonded to s1, may include s1.',
  337. '@examples': ['BOUND_TO name CA'],
  338. name: 'bound_to',
  339. abbr: ['bto.'],
  340. type: h.prefix,
  341. rule: h.prefixOp(/BOUND_TO|bto\./i),
  342. map: (op: string, selection: Expression) => {
  343. return h.asAtoms(
  344. B.struct.modifier.includeConnected({
  345. '0': B.struct.modifier.union({ 0: selection }),
  346. })
  347. );
  348. },
  349. },
  350. {
  351. '@desc': 'Extends s1 by X bonds connected to atoms in s1.',
  352. '@examples': ['resname LIG EXTEND 3'],
  353. name: 'extend',
  354. abbr: ['xt.'],
  355. type: h.postfix,
  356. rule: h.postfixOp(/(EXTEND|xt\.)\s+([0-9]+)/i, 2).map((x: any) => parseInt(x)),
  357. map: (count: number, selection: Expression) => {
  358. return h.asAtoms(
  359. B.struct.modifier.includeConnected({
  360. '0': B.struct.modifier.union({ 0: selection }),
  361. 'bond-test': true,
  362. 'layer-count': count,
  363. })
  364. );
  365. },
  366. },
  367. ];