react-cheminfo 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/README.md +39 -26
  2. package/lib/chrome/ui/HeaderToggle.d.ts +52 -0
  3. package/lib/chrome/ui/HeaderToggle.d.ts.map +1 -0
  4. package/lib/chrome/ui/HeaderToggle.js +31 -0
  5. package/lib/chrome/ui/HeaderToggle.js.map +1 -0
  6. package/lib/chrome/ui/index.d.ts +2 -0
  7. package/lib/chrome/ui/index.d.ts.map +1 -1
  8. package/lib/chrome/ui/index.js +1 -0
  9. package/lib/chrome/ui/index.js.map +1 -1
  10. package/lib/citation/core/gfn2Paper.d.ts +10 -0
  11. package/lib/citation/core/gfn2Paper.d.ts.map +1 -0
  12. package/lib/citation/core/gfn2Paper.js +28 -0
  13. package/lib/citation/core/gfn2Paper.js.map +1 -0
  14. package/lib/citation/core/index.d.ts +1 -0
  15. package/lib/citation/core/index.d.ts.map +1 -1
  16. package/lib/citation/core/index.js +1 -0
  17. package/lib/citation/core/index.js.map +1 -1
  18. package/lib/clipboard/ui/ClickToCopy.d.ts +8 -7
  19. package/lib/clipboard/ui/ClickToCopy.d.ts.map +1 -1
  20. package/lib/clipboard/ui/ClickToCopy.js +3 -3
  21. package/lib/clipboard/ui/ClickToCopy.js.map +1 -1
  22. package/lib/conformers.d.ts +27 -0
  23. package/lib/conformers.d.ts.map +1 -0
  24. package/lib/conformers.js +19 -0
  25. package/lib/conformers.js.map +1 -0
  26. package/lib/core.d.ts +1 -0
  27. package/lib/core.d.ts.map +1 -1
  28. package/lib/core.js +1 -0
  29. package/lib/core.js.map +1 -1
  30. package/lib/credits/core/credits.d.ts +12 -0
  31. package/lib/credits/core/credits.d.ts.map +1 -1
  32. package/lib/credits/core/credits.js +14 -0
  33. package/lib/credits/core/credits.js.map +1 -1
  34. package/lib/ecosystem/core/index.d.ts +1 -1
  35. package/lib/ecosystem/core/index.d.ts.map +1 -1
  36. package/lib/ecosystem/core/index.js.map +1 -1
  37. package/lib/ecosystem/core/sites.d.ts +11 -1
  38. package/lib/ecosystem/core/sites.d.ts.map +1 -1
  39. package/lib/ecosystem/core/sites.js +11 -4
  40. package/lib/ecosystem/core/sites.js.map +1 -1
  41. package/lib/ecosystem/ui/EcosystemLinks.d.ts.map +1 -1
  42. package/lib/ecosystem/ui/EcosystemLinks.js +3 -1
  43. package/lib/ecosystem/ui/EcosystemLinks.js.map +1 -1
  44. package/lib/ecosystem/ui/SiteTile.d.ts.map +1 -1
  45. package/lib/ecosystem/ui/SiteTile.js +3 -1
  46. package/lib/ecosystem/ui/SiteTile.js.map +1 -1
  47. package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
  48. package/lib/ecosystem/ui/glyphs.js +6 -5
  49. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  50. package/lib/format/core/index.d.ts +1 -1
  51. package/lib/format/core/index.d.ts.map +1 -1
  52. package/lib/format/core/index.js +1 -1
  53. package/lib/format/core/index.js.map +1 -1
  54. package/lib/format/core/words.d.ts +9 -0
  55. package/lib/format/core/words.d.ts.map +1 -1
  56. package/lib/format/core/words.js +16 -0
  57. package/lib/format/core/words.js.map +1 -1
  58. package/lib/language/core/index.d.ts +3 -0
  59. package/lib/language/core/index.d.ts.map +1 -0
  60. package/lib/language/core/index.js +3 -0
  61. package/lib/language/core/index.js.map +1 -0
  62. package/lib/language/core/languageName.d.ts +8 -0
  63. package/lib/language/core/languageName.d.ts.map +1 -0
  64. package/lib/language/core/languageName.js +16 -0
  65. package/lib/language/core/languageName.js.map +1 -0
  66. package/lib/language/core/languageParam.d.ts +35 -0
  67. package/lib/language/core/languageParam.d.ts.map +1 -0
  68. package/lib/language/core/languageParam.js +67 -0
  69. package/lib/language/core/languageParam.js.map +1 -0
  70. package/lib/language/ui/SiteLanguage.d.ts +20 -0
  71. package/lib/language/ui/SiteLanguage.d.ts.map +1 -0
  72. package/lib/language/ui/SiteLanguage.js +14 -0
  73. package/lib/language/ui/SiteLanguage.js.map +1 -0
  74. package/lib/language/ui/index.d.ts +4 -0
  75. package/lib/language/ui/index.d.ts.map +1 -0
  76. package/lib/language/ui/index.js +3 -0
  77. package/lib/language/ui/index.js.map +1 -0
  78. package/lib/language/ui/siteLanguageContext.d.ts +12 -0
  79. package/lib/language/ui/siteLanguageContext.d.ts.map +1 -0
  80. package/lib/language/ui/siteLanguageContext.js +19 -0
  81. package/lib/language/ui/siteLanguageContext.js.map +1 -0
  82. package/lib/orbital/core/atomicOrbitals.d.ts +9 -1
  83. package/lib/orbital/core/atomicOrbitals.d.ts.map +1 -1
  84. package/lib/orbital/core/atomicOrbitals.js +7 -0
  85. package/lib/orbital/core/atomicOrbitals.js.map +1 -1
  86. package/lib/orbital/core/hydrogenic.d.ts +2 -1
  87. package/lib/orbital/core/hydrogenic.d.ts.map +1 -1
  88. package/lib/orbital/core/hydrogenic.js +2 -1
  89. package/lib/orbital/core/hydrogenic.js.map +1 -1
  90. package/lib/orbital/core/index.d.ts +1 -0
  91. package/lib/orbital/core/index.d.ts.map +1 -1
  92. package/lib/orbital/core/index.js +1 -0
  93. package/lib/orbital/core/index.js.map +1 -1
  94. package/lib/orbital/core/slaterEnergy.d.ts +35 -0
  95. package/lib/orbital/core/slaterEnergy.d.ts.map +1 -0
  96. package/lib/orbital/core/slaterEnergy.js +66 -0
  97. package/lib/orbital/core/slaterEnergy.js.map +1 -0
  98. package/lib/shared/ui/MenuButton.d.ts +3 -2
  99. package/lib/shared/ui/MenuButton.d.ts.map +1 -1
  100. package/lib/shared/ui/MenuButton.js +1 -1
  101. package/lib/shared/ui/MenuButton.js.map +1 -1
  102. package/lib/structure/core/conformerMinimise.d.ts +36 -0
  103. package/lib/structure/core/conformerMinimise.d.ts.map +1 -0
  104. package/lib/structure/core/conformerMinimise.js +81 -0
  105. package/lib/structure/core/conformerMinimise.js.map +1 -0
  106. package/lib/structure/core/conformerMinimum.d.ts +62 -0
  107. package/lib/structure/core/conformerMinimum.d.ts.map +1 -0
  108. package/lib/structure/core/conformerMinimum.js +90 -0
  109. package/lib/structure/core/conformerMinimum.js.map +1 -0
  110. package/lib/structure/core/conformerOptions.d.ts +62 -0
  111. package/lib/structure/core/conformerOptions.d.ts.map +1 -0
  112. package/lib/structure/core/conformerOptions.js +83 -0
  113. package/lib/structure/core/conformerOptions.js.map +1 -0
  114. package/lib/structure/core/conformerRefine.d.ts +104 -0
  115. package/lib/structure/core/conformerRefine.d.ts.map +1 -0
  116. package/lib/structure/core/conformerRefine.js +130 -0
  117. package/lib/structure/core/conformerRefine.js.map +1 -0
  118. package/lib/structure/core/conformerRefineRank.d.ts +36 -0
  119. package/lib/structure/core/conformerRefineRank.d.ts.map +1 -0
  120. package/lib/structure/core/conformerRefineRank.js +69 -0
  121. package/lib/structure/core/conformerRefineRank.js.map +1 -0
  122. package/lib/structure/core/conformerSession.d.ts +50 -0
  123. package/lib/structure/core/conformerSession.d.ts.map +1 -0
  124. package/lib/structure/core/conformerSession.js +88 -0
  125. package/lib/structure/core/conformerSession.js.map +1 -0
  126. package/lib/structure/core/conformerShape.d.ts +36 -0
  127. package/lib/structure/core/conformerShape.d.ts.map +1 -0
  128. package/lib/structure/core/conformerShape.js +150 -0
  129. package/lib/structure/core/conformerShape.js.map +1 -0
  130. package/lib/structure/core/conformers.d.ts +107 -0
  131. package/lib/structure/core/conformers.d.ts.map +1 -0
  132. package/lib/structure/core/conformers.js +149 -0
  133. package/lib/structure/core/conformers.js.map +1 -0
  134. package/lib/structure/core/geometryRelaxer.d.ts +66 -0
  135. package/lib/structure/core/geometryRelaxer.d.ts.map +1 -0
  136. package/lib/structure/core/geometryRelaxer.js +2 -0
  137. package/lib/structure/core/geometryRelaxer.js.map +1 -0
  138. package/lib/structure/core/index.d.ts +4 -0
  139. package/lib/structure/core/index.d.ts.map +1 -1
  140. package/lib/structure/core/index.js +2 -0
  141. package/lib/structure/core/index.js.map +1 -1
  142. package/lib/structure/core/moleculeCoordinates.d.ts +31 -0
  143. package/lib/structure/core/moleculeCoordinates.d.ts.map +1 -0
  144. package/lib/structure/core/moleculeCoordinates.js +80 -0
  145. package/lib/structure/core/moleculeCoordinates.js.map +1 -0
  146. package/lib/structure/core/molfileExport.d.ts +42 -0
  147. package/lib/structure/core/molfileExport.d.ts.map +1 -0
  148. package/lib/structure/core/molfileExport.js +47 -0
  149. package/lib/structure/core/molfileExport.js.map +1 -0
  150. package/lib/structure/core/oclResources.d.ts +12 -0
  151. package/lib/structure/core/oclResources.d.ts.map +1 -0
  152. package/lib/structure/core/oclResources.js +35 -0
  153. package/lib/structure/core/oclResources.js.map +1 -0
  154. package/lib/translate/core/catalog.d.ts +61 -0
  155. package/lib/translate/core/catalog.d.ts.map +1 -0
  156. package/lib/translate/core/catalog.js +49 -0
  157. package/lib/translate/core/catalog.js.map +1 -0
  158. package/lib/translate/core/checkTranslation.d.ts +32 -0
  159. package/lib/translate/core/checkTranslation.d.ts.map +1 -0
  160. package/lib/translate/core/checkTranslation.js +98 -0
  161. package/lib/translate/core/checkTranslation.js.map +1 -0
  162. package/lib/translate/core/contribution.d.ts +88 -0
  163. package/lib/translate/core/contribution.d.ts.map +1 -0
  164. package/lib/translate/core/contribution.js +14 -0
  165. package/lib/translate/core/contribution.js.map +1 -0
  166. package/lib/translate/core/index.d.ts +19 -0
  167. package/lib/translate/core/index.d.ts.map +1 -0
  168. package/lib/translate/core/index.js +15 -0
  169. package/lib/translate/core/index.js.map +1 -0
  170. package/lib/translate/core/marker.d.ts +45 -0
  171. package/lib/translate/core/marker.d.ts.map +1 -0
  172. package/lib/translate/core/marker.js +79 -0
  173. package/lib/translate/core/marker.js.map +1 -0
  174. package/lib/translate/core/mergeMessages.d.ts +35 -0
  175. package/lib/translate/core/mergeMessages.d.ts.map +1 -0
  176. package/lib/translate/core/mergeMessages.js +69 -0
  177. package/lib/translate/core/mergeMessages.js.map +1 -0
  178. package/lib/translate/core/session.d.ts +102 -0
  179. package/lib/translate/core/session.d.ts.map +1 -0
  180. package/lib/translate/core/session.js +162 -0
  181. package/lib/translate/core/session.js.map +1 -0
  182. package/lib/translate/core/start.d.ts +103 -0
  183. package/lib/translate/core/start.d.ts.map +1 -0
  184. package/lib/translate/core/start.js +92 -0
  185. package/lib/translate/core/start.js.map +1 -0
  186. package/lib/translate/core/suggestions.d.ts +77 -0
  187. package/lib/translate/core/suggestions.d.ts.map +1 -0
  188. package/lib/translate/core/suggestions.js +143 -0
  189. package/lib/translate/core/suggestions.js.map +1 -0
  190. package/lib/translate/core/tables.d.ts +100 -0
  191. package/lib/translate/core/tables.d.ts.map +1 -0
  192. package/lib/translate/core/tables.js +51 -0
  193. package/lib/translate/core/tables.js.map +1 -0
  194. package/lib/translate.d.ts +2 -0
  195. package/lib/translate.d.ts.map +1 -0
  196. package/lib/translate.js +6 -0
  197. package/lib/translate.js.map +1 -0
  198. package/lib/ui.d.ts +1 -0
  199. package/lib/ui.d.ts.map +1 -1
  200. package/lib/ui.js +1 -0
  201. package/lib/ui.js.map +1 -1
  202. package/lib/xtb/core/xtbRelaxer.d.ts +61 -0
  203. package/lib/xtb/core/xtbRelaxer.d.ts.map +1 -0
  204. package/lib/xtb/core/xtbRelaxer.js +108 -0
  205. package/lib/xtb/core/xtbRelaxer.js.map +1 -0
  206. package/lib/xtb.d.ts +11 -0
  207. package/lib/xtb.d.ts.map +1 -0
  208. package/lib/xtb.js +10 -0
  209. package/lib/xtb.js.map +1 -0
  210. package/package.json +18 -11
  211. package/src/chrome/ui/HeaderToggle.tsx +100 -0
  212. package/src/chrome/ui/index.ts +2 -0
  213. package/src/citation/core/gfn2Paper.ts +32 -0
  214. package/src/citation/core/index.ts +1 -0
  215. package/src/clipboard/ui/ClickToCopy.tsx +8 -7
  216. package/src/conformers.ts +87 -0
  217. package/src/core.ts +1 -0
  218. package/src/credits/core/credits.ts +16 -0
  219. package/src/ecosystem/core/index.ts +1 -0
  220. package/src/ecosystem/core/sites.ts +24 -4
  221. package/src/ecosystem/ui/EcosystemLinks.tsx +3 -1
  222. package/src/ecosystem/ui/SiteTile.tsx +3 -1
  223. package/src/ecosystem/ui/glyphs.tsx +16 -22
  224. package/src/format/core/index.ts +1 -1
  225. package/src/format/core/words.ts +19 -0
  226. package/src/language/core/index.ts +7 -0
  227. package/src/language/core/languageName.ts +16 -0
  228. package/src/language/core/languageParam.ts +70 -0
  229. package/src/language/ui/SiteLanguage.tsx +26 -0
  230. package/src/language/ui/index.ts +3 -0
  231. package/src/language/ui/siteLanguageContext.ts +20 -0
  232. package/src/orbital/core/atomicOrbitals.ts +17 -1
  233. package/src/orbital/core/hydrogenic.ts +2 -1
  234. package/src/orbital/core/index.ts +1 -0
  235. package/src/orbital/core/slaterEnergy.ts +83 -0
  236. package/src/shared/ui/MenuButton.tsx +4 -3
  237. package/src/structure/core/conformerMinimise.ts +99 -0
  238. package/src/structure/core/conformerMinimum.ts +125 -0
  239. package/src/structure/core/conformerOptions.ts +125 -0
  240. package/src/structure/core/conformerRefine.ts +234 -0
  241. package/src/structure/core/conformerRefineRank.ts +89 -0
  242. package/src/structure/core/conformerSession.ts +123 -0
  243. package/src/structure/core/conformerShape.ts +181 -0
  244. package/src/structure/core/conformers.ts +282 -0
  245. package/src/structure/core/geometryRelaxer.ts +71 -0
  246. package/src/structure/core/index.ts +13 -0
  247. package/src/structure/core/moleculeCoordinates.ts +93 -0
  248. package/src/structure/core/molfileExport.ts +62 -0
  249. package/src/structure/core/oclResources.ts +41 -0
  250. package/src/translate/core/catalog.ts +83 -0
  251. package/src/translate/core/checkTranslation.ts +128 -0
  252. package/src/translate/core/contribution.ts +99 -0
  253. package/src/translate/core/index.ts +72 -0
  254. package/src/translate/core/marker.ts +85 -0
  255. package/src/translate/core/mergeMessages.ts +74 -0
  256. package/src/translate/core/session.ts +250 -0
  257. package/src/translate/core/start.ts +183 -0
  258. package/src/translate/core/suggestions.ts +211 -0
  259. package/src/translate/core/tables.ts +120 -0
  260. package/src/translate.ts +5 -0
  261. package/src/ui.ts +1 -0
  262. package/src/xtb/core/xtbRelaxer.ts +160 -0
  263. package/src/xtb.ts +16 -0
  264. package/styles/chrome.css +61 -30
@@ -0,0 +1,90 @@
1
+ import { Molecule } from 'openchemlib';
2
+ import { conformerShape } from "./conformerShape.js";
3
+ /**
4
+ * Energy gap, in kcal/mol, under which two minimised conformers can be the
5
+ * same minimum. Copies of one minimum agree to about 1e-7; distinct minima
6
+ * that close in energy differ in shape by more than a percent.
7
+ */
8
+ export const SAME_ENERGY_TOLERANCE = 1e-3;
9
+ /** Gap between principal moments, relative to the largest, under which two shapes are one. */
10
+ export const SAME_SHAPE_TOLERANCE = 1e-3;
11
+ /**
12
+ * Gap in chirality under which two shapes are one. Copies of one minimum agree
13
+ * to about 1e-4 and a shape that is its own mirror image reads within 1e-3 of
14
+ * zero, while the mirror images of a chiral minimum usually sit tenths apart.
15
+ */
16
+ export const SAME_CHIRALITY_TOLERANCE = 1e-2;
17
+ /**
18
+ * Whether two minimised conformers are one energy minimum: the same energy,
19
+ * the same principal moments and the same chirality. The two mirror-image
20
+ * gauche butanes are two minima; the two chairs of cyclohexane, each its own
21
+ * mirror image, are one.
22
+ * @param energyA - Total energy of the first conformer, kcal/mol.
23
+ * @param shapeA - Shape of the first conformer.
24
+ * @param energyB - Total energy of the second conformer, kcal/mol.
25
+ * @param shapeB - Shape of the second conformer.
26
+ * @returns `true` when both agree within {@link SAME_ENERGY_TOLERANCE}, {@link SAME_SHAPE_TOLERANCE} and {@link SAME_CHIRALITY_TOLERANCE}.
27
+ */
28
+ export function isSameMinimum(energyA, shapeA, energyB, shapeB) {
29
+ if (Math.abs(energyA - energyB) > SAME_ENERGY_TOLERANCE)
30
+ return false;
31
+ return isSameShape(shapeA, shapeB);
32
+ }
33
+ /**
34
+ * Whether two conformers have the same shape: the same principal moments and
35
+ * the same chirality, energies not consulted. Two mirror images share the
36
+ * moments and not the chirality, so they are two shapes.
37
+ * @param shapeA - Shape of one conformer.
38
+ * @param shapeB - Shape of the other.
39
+ * @returns `true` when both agree within {@link SAME_SHAPE_TOLERANCE} and {@link SAME_CHIRALITY_TOLERANCE}.
40
+ */
41
+ export function isSameShape(shapeA, shapeB) {
42
+ if (Math.abs(shapeA.chirality - shapeB.chirality) > SAME_CHIRALITY_TOLERANCE) {
43
+ return false;
44
+ }
45
+ const momentsA = shapeA.moments;
46
+ const momentsB = shapeB.moments;
47
+ const scale = Math.max(momentsA[2], momentsB[2]);
48
+ for (let axis = 0; axis < 3; axis++) {
49
+ const gap = Math.abs((momentsA[axis] ?? 0) - (momentsB[axis] ?? 0));
50
+ if (gap > SAME_SHAPE_TOLERANCE * scale)
51
+ return false;
52
+ }
53
+ return true;
54
+ }
55
+ /**
56
+ * Whether a minimised conformer lands in a minimum one of `kept` already holds.
57
+ * @param energy - Total energy of the candidate, kcal/mol.
58
+ * @param shape - Shape of the candidate.
59
+ * @param kept - The conformers kept so far.
60
+ * @param keptShapes - Their shapes, index for index; `null` where there is no energy.
61
+ * @returns `true` when one of them is the same minimum.
62
+ */
63
+ export function isKeptMinimum(energy, shape, kept, keptShapes) {
64
+ for (let index = 0; index < kept.length; index++) {
65
+ const keptEnergy = kept[index]?.energy ?? null;
66
+ const keptShape = keptShapes[index] ?? null;
67
+ if (keptEnergy !== null &&
68
+ keptShape !== null &&
69
+ isSameMinimum(energy, shape, keptEnergy, keptShape)) {
70
+ return true;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+ /**
76
+ * Shapes of conformers read back from their molfiles, so a continuation
77
+ * compares new conformers with the ones a previous run kept.
78
+ * @param conformers - Conformers of an earlier run.
79
+ * @returns Their shapes, index for index; `null` for one without an energy.
80
+ */
81
+ export function minimisedShapes(conformers) {
82
+ const shapes = [];
83
+ for (const conformer of conformers) {
84
+ shapes.push(conformer.energy === null
85
+ ? null
86
+ : conformerShape(Molecule.fromMolfile(conformer.molfile.data)));
87
+ }
88
+ return shapes;
89
+ }
90
+ //# sourceMappingURL=conformerMinimum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformerMinimum.js","sourceRoot":"","sources":["../../../src/structure/core/conformerMinimum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAUrD;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAE1C,8FAA8F;AAC9F,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAEzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,MAAsB,EACtB,OAAe,EACf,MAAsB;IAEtB,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,qBAAqB;QAAE,OAAO,KAAK,CAAC;IACtE,OAAO,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,MAAsB,EACtB,MAAsB;IAEtB,IACE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,wBAAwB,EACxE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;IAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpE,IAAI,GAAG,GAAG,oBAAoB,GAAG,KAAK;YAAE,OAAO,KAAK,CAAC;IACvD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,KAAqB,EACrB,IAA8B,EAC9B,UAAgD;IAEhD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACjD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,IAAI,CAAC;QAC/C,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;QAC5C,IACE,UAAU,KAAK,IAAI;YACnB,SAAS,KAAK,IAAI;YAClB,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,EACnD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAoC;IAEpC,MAAM,MAAM,GAAiC,EAAE,CAAC;IAChD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CACT,SAAS,CAAC,MAAM,KAAK,IAAI;YACvB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,62 @@
1
+ /** The four torsion-set strategies OpenChemLib's `ConformerGenerator` offers. */
2
+ export declare const CONFORMER_STRATEGIES: readonly ["adaptive-random", "likely-random", "pure-random", "likely-systematic"];
3
+ /** One of {@link CONFORMER_STRATEGIES}. */
4
+ export type ConformerStrategy = (typeof CONFORMER_STRATEGIES)[number];
5
+ /** Short names for the strategy picker. */
6
+ export declare const STRATEGY_LABELS: Record<ConformerStrategy, string>;
7
+ /** One sentence per strategy, shown as help text next to the picker. */
8
+ export declare const STRATEGY_DETAILS: Record<ConformerStrategy, string>;
9
+ /** Force fields offered for the optional minimisation step. */
10
+ export declare const MINIMISATION_ALGORITHMS: readonly ["none", "MMFF94", "MMFF94s", "MMFF94s+"];
11
+ /** One of {@link MINIMISATION_ALGORITHMS}. */
12
+ export type MinimisationAlgorithm = (typeof MINIMISATION_ALGORITHMS)[number];
13
+ /** Short names for the minimisation picker. */
14
+ export declare const MINIMISATION_LABELS: Record<MinimisationAlgorithm, string>;
15
+ /** Everything a conformer run needs, in one serialisable object. */
16
+ export interface ConformerOptions {
17
+ /** Which torsion-set strategy the generator follows. */
18
+ strategy: ConformerStrategy;
19
+ /** Upper bound on the number of conformers kept. */
20
+ maxConformers: number;
21
+ /** Upper bound on the distinct torsion sets the strategy will try. */
22
+ maxTorsionSets: number;
23
+ /** Rotate every rotatable bond in 60° steps instead of using the torsion database. */
24
+ use60DegreeSteps: boolean;
25
+ /** Seed of the generator's random source, so a run is reproducible. */
26
+ seed: number;
27
+ /** Force field used to relax each conformer; `none` leaves the energies unknown. */
28
+ minimisation: MinimisationAlgorithm;
29
+ /** Iteration budget of a single minimisation. */
30
+ maxIterations: number;
31
+ /** Wall-clock budget of the whole run, in seconds. */
32
+ timeoutSeconds: number;
33
+ }
34
+ /** The settings a first-time visitor gets. */
35
+ export declare const DEFAULT_CONFORMER_OPTIONS: ConformerOptions;
36
+ /**
37
+ * Whether two option sets would run the same calculation.
38
+ * @param first - One option set.
39
+ * @param second - The other.
40
+ * @returns `true` when every option is equal.
41
+ */
42
+ export declare function sameConformerOptions(first: ConformerOptions, second: ConformerOptions): boolean;
43
+ /**
44
+ * Rotatable-bond count above which the conformer space is large enough that the
45
+ * run is worth warning about before it starts.
46
+ */
47
+ export declare const ROTATABLE_BOND_WARNING = 12;
48
+ /**
49
+ * Narrow an arbitrary string, typically a URL parameter or a stored preference,
50
+ * to a strategy id.
51
+ * @param value - Text to test.
52
+ * @returns Whether `value` is one of {@link CONFORMER_STRATEGIES}.
53
+ */
54
+ export declare function isConformerStrategy(value: string): value is ConformerStrategy;
55
+ /**
56
+ * Narrow an arbitrary string, typically a stored preference, to a force-field
57
+ * id.
58
+ * @param value - Text to test.
59
+ * @returns Whether `value` is one of {@link MINIMISATION_ALGORITHMS}.
60
+ */
61
+ export declare function isMinimisationAlgorithm(value: string): value is MinimisationAlgorithm;
62
+ //# sourceMappingURL=conformerOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformerOptions.d.ts","sourceRoot":"","sources":["../../../src/structure/core/conformerOptions.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,mFAKvB,CAAC;AAEX,2CAA2C;AAC3C,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,2CAA2C;AAC3C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAK7D,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAS9D,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,uBAAuB,oDAK1B,CAAC;AAEX,8CAA8C;AAC9C,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7E,+CAA+C;AAC/C,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAKrE,CAAC;AAEF,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,sFAAsF;IACtF,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,YAAY,EAAE,qBAAqB,CAAC;IACpC,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,8CAA8C;AAC9C,eAAO,MAAM,yBAAyB,EAAE,gBASvC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAKT;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,iBAAiB,CAE7E;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,GACZ,KAAK,IAAI,qBAAqB,CAEhC"}
@@ -0,0 +1,83 @@
1
+ /** The four torsion-set strategies OpenChemLib's `ConformerGenerator` offers. */
2
+ export const CONFORMER_STRATEGIES = [
3
+ 'adaptive-random',
4
+ 'likely-random',
5
+ 'pure-random',
6
+ 'likely-systematic',
7
+ ];
8
+ /** Short names for the strategy picker. */
9
+ export const STRATEGY_LABELS = {
10
+ 'adaptive-random': 'Adaptive random',
11
+ 'likely-random': 'Likely random',
12
+ 'pure-random': 'Pure random',
13
+ 'likely-systematic': 'Likely systematic',
14
+ };
15
+ /** One sentence per strategy, shown as help text next to the picker. */
16
+ export const STRATEGY_DETAILS = {
17
+ 'adaptive-random': 'Draws torsions at random but steers away from the combinations that already collided, which covers a flexible molecule fastest.',
18
+ 'likely-random': 'Draws torsions at random from the torsion database, weighted by how often each angle is seen in crystal structures.',
19
+ 'pure-random': 'Draws every torsion uniformly at random, ignoring how likely the angle is — the widest spread and the least realistic set.',
20
+ 'likely-systematic': 'Walks the torsion combinations in decreasing likelihood, so the first conformers are the most probable ones and the set is reproducible.',
21
+ };
22
+ /** Force fields offered for the optional minimisation step. */
23
+ export const MINIMISATION_ALGORITHMS = [
24
+ 'none',
25
+ 'MMFF94',
26
+ 'MMFF94s',
27
+ 'MMFF94s+',
28
+ ];
29
+ /** Short names for the minimisation picker. */
30
+ export const MINIMISATION_LABELS = {
31
+ none: 'None (no energies)',
32
+ MMFF94: 'MMFF94',
33
+ MMFF94s: 'MMFF94s',
34
+ 'MMFF94s+': 'MMFF94s+',
35
+ };
36
+ /** The settings a first-time visitor gets. */
37
+ export const DEFAULT_CONFORMER_OPTIONS = {
38
+ strategy: 'adaptive-random',
39
+ maxConformers: 10,
40
+ maxTorsionSets: 10_000,
41
+ use60DegreeSteps: false,
42
+ seed: 42,
43
+ minimisation: 'MMFF94s+',
44
+ maxIterations: 4000,
45
+ timeoutSeconds: 10,
46
+ };
47
+ /**
48
+ * Whether two option sets would run the same calculation.
49
+ * @param first - One option set.
50
+ * @param second - The other.
51
+ * @returns `true` when every option is equal.
52
+ */
53
+ export function sameConformerOptions(first, second) {
54
+ for (const key of Object.keys(first)) {
55
+ if (first[key] !== second[key])
56
+ return false;
57
+ }
58
+ return true;
59
+ }
60
+ /**
61
+ * Rotatable-bond count above which the conformer space is large enough that the
62
+ * run is worth warning about before it starts.
63
+ */
64
+ export const ROTATABLE_BOND_WARNING = 12;
65
+ /**
66
+ * Narrow an arbitrary string, typically a URL parameter or a stored preference,
67
+ * to a strategy id.
68
+ * @param value - Text to test.
69
+ * @returns Whether `value` is one of {@link CONFORMER_STRATEGIES}.
70
+ */
71
+ export function isConformerStrategy(value) {
72
+ return CONFORMER_STRATEGIES.includes(value);
73
+ }
74
+ /**
75
+ * Narrow an arbitrary string, typically a stored preference, to a force-field
76
+ * id.
77
+ * @param value - Text to test.
78
+ * @returns Whether `value` is one of {@link MINIMISATION_ALGORITHMS}.
79
+ */
80
+ export function isMinimisationAlgorithm(value) {
81
+ return MINIMISATION_ALGORITHMS.includes(value);
82
+ }
83
+ //# sourceMappingURL=conformerOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformerOptions.js","sourceRoot":"","sources":["../../../src/structure/core/conformerOptions.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,iBAAiB;IACjB,eAAe;IACf,aAAa;IACb,mBAAmB;CACX,CAAC;AAKX,2CAA2C;AAC3C,MAAM,CAAC,MAAM,eAAe,GAAsC;IAChE,iBAAiB,EAAE,iBAAiB;IACpC,eAAe,EAAE,eAAe;IAChC,aAAa,EAAE,aAAa;IAC5B,mBAAmB,EAAE,mBAAmB;CACzC,CAAC;AAEF,wEAAwE;AACxE,MAAM,CAAC,MAAM,gBAAgB,GAAsC;IACjE,iBAAiB,EACf,iIAAiI;IACnI,eAAe,EACb,qHAAqH;IACvH,aAAa,EACX,4HAA4H;IAC9H,mBAAmB,EACjB,0IAA0I;CAC7I,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,MAAM;IACN,QAAQ;IACR,SAAS;IACT,UAAU;CACF,CAAC;AAKX,+CAA+C;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAA0C;IACxE,IAAI,EAAE,oBAAoB;IAC1B,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,UAAU,EAAE,UAAU;CACvB,CAAC;AAsBF,8CAA8C;AAC9C,MAAM,CAAC,MAAM,yBAAyB,GAAqB;IACzD,QAAQ,EAAE,iBAAiB;IAC3B,aAAa,EAAE,EAAE;IACjB,cAAc,EAAE,MAAM;IACtB,gBAAgB,EAAE,KAAK;IACvB,IAAI,EAAE,EAAE;IACR,YAAY,EAAE,UAAU;IACxB,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,EAAE;CACnB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAuB,EACvB,MAAwB;IAExB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAkC,EAAE,CAAC;QACtE,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAQ,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAa;IAEb,OAAQ,uBAA6C,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,104 @@
1
+ import type { ConformerSet } from './conformers.ts';
2
+ import type { GeometryRelaxer, RelaxerOptions } from './geometryRelaxer.ts';
3
+ /** What refining one conformer produced, on top of its force-field numbers. */
4
+ export interface ConformerRefinement {
5
+ /**
6
+ * The rank this conformer held before the refinement, which is the `id` it
7
+ * carried in the force-field set. Refinement hands the ids out again, so this
8
+ * is what a reader compares the new rank against and what a caller uses to
9
+ * find again the conformer that was on screen.
10
+ */
11
+ forceFieldId: number;
12
+ /** Total energy at the relaxed geometry, kcal/mol. */
13
+ energy: number;
14
+ /** Energy above the most stable refined conformer of the set, kcal/mol. */
15
+ relativeEnergy: number;
16
+ /**
17
+ * The dispersion term of {@link ConformerRefinement.energy}, kcal/mol, or
18
+ * `null` from a method that has none.
19
+ */
20
+ dispersionEnergy: number | null;
21
+ /**
22
+ * The force-field energy this conformer had before, kcal/mol, `null` when it
23
+ * had none. Kept so the two rankings can be read against each other, which is
24
+ * the whole reason to refine.
25
+ */
26
+ forceFieldEnergy: number | null;
27
+ /** How far the atoms moved from the force-field geometry, RMSD in ångström. */
28
+ rmsd: number;
29
+ /** Optimizer cycles spent. */
30
+ cycles: number;
31
+ /** Whether the optimizer met its convergence criteria. */
32
+ converged: boolean;
33
+ }
34
+ /** What a whole refinement pass produced, recorded on the set. */
35
+ export interface ConformerSetRefinement {
36
+ /** The method, as it is shown to a reader, e.g. `GFN2-xTB`. */
37
+ method: string;
38
+ /** Wall-clock duration of the pass, in milliseconds. */
39
+ elapsedMilliseconds: number;
40
+ /** How many conformers were dropped as duplicates of a kept minimum. */
41
+ merged: number;
42
+ /**
43
+ * How many conformers the refined ranking put at a different position,
44
+ * degenerate conformers swapping places included — so it is a fact about the
45
+ * two orders, not a measure of how much the methods disagree. The two relative
46
+ * energies are what a reader should be shown.
47
+ */
48
+ reordered: number;
49
+ }
50
+ /** How a refinement pass is tuned, watched and cancelled. */
51
+ export interface RefineConformersOptions extends RelaxerOptions {
52
+ /**
53
+ * Name of the method, for the label the UI shows.
54
+ * @default 'GFN2-xTB'
55
+ */
56
+ method?: string;
57
+ /**
58
+ * Energy gap in kcal/mol under which two refined conformers are taken to be
59
+ * one minimum.
60
+ * @default REFINED_SAME_ENERGY_TOLERANCE
61
+ */
62
+ sameEnergyTolerance?: number;
63
+ }
64
+ /**
65
+ * Energy gap in kcal/mol under which two relaxed conformers can be one minimum.
66
+ *
67
+ * Measured with `benchmark/conformerRefine.js scatter`: one butane minimum
68
+ * reached from six geometries perturbed by up to 0.02 Å lands within 4.9e-6
69
+ * kcal/mol, in 8 to 20 optimizer cycles. So 1e-4 is twenty times the scatter of
70
+ * one minimum, and ten times below the closest gap between two real minima seen
71
+ * on a flexible molecule — ibuprofen has distinct conformers 1e-3 kcal/mol
72
+ * apart. The energy is only the cheap half of the test in any case: what decides
73
+ * that two conformers are one minimum is the shape.
74
+ */
75
+ export declare const REFINED_SAME_ENERGY_TOLERANCE = 0.0001;
76
+ /** What is shown when a conformer set is ranked by the refined energies. */
77
+ export declare const REFINED_RANKING = "refined";
78
+ /**
79
+ * Relax every conformer of a set with a better method and rank the set by what
80
+ * that method says.
81
+ *
82
+ * This is the opt-in second stage of a conformer search, opt-in because it costs
83
+ * seconds where the force field costs milliseconds. MMFF94 places the atoms well
84
+ * but ranks conformers badly — it has no dispersion and no electronic structure,
85
+ * and those are what decide which conformer is lowest. So both numbers are kept:
86
+ * `conformer.energy` stays the force field's, `conformer.refinement.energy` is
87
+ * the new one, and `set.rankedBy` says which of the two the order follows.
88
+ *
89
+ * Refinement changes the set, not only its order: conformers the force field
90
+ * held apart can relax into one minimum, and the first of them is the one that
91
+ * survives. Ids are handed out again over the ranked result, so a caller holding
92
+ * an id must look its conformer up again.
93
+ *
94
+ * A conformer without a force-field energy is refined like any other; its
95
+ * `forceFieldEnergy` is `null` and it takes its place by the refined energy.
96
+ * @param set - The set to refine. Not modified.
97
+ * @param relax - The engine, e.g. `xtbRelaxer` from `react-cheminfo/xtb`.
98
+ * @param options - Method name, duplicate tolerance, progress and cancellation.
99
+ * @returns A new set, ranked by the refined energies.
100
+ * @throws {Error} When the relaxer fails or is cancelled, or answers with the
101
+ * wrong number of geometries.
102
+ */
103
+ export declare function refineConformers(set: ConformerSet, relax: GeometryRelaxer, options?: RefineConformersOptions): Promise<ConformerSet>;
104
+ //# sourceMappingURL=conformerRefine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformerRefine.d.ts","sourceRoot":"","sources":["../../../src/structure/core/conformerRefine.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAa,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,EACf,MAAM,sBAAsB,CAAC;AAQ9B,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,cAAc,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6DAA6D;AAC7D,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC7D;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B,SAAO,CAAC;AAElD,4EAA4E;AAC5E,eAAO,MAAM,eAAe,YAAY,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,YAAY,EACjB,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,YAAY,CAAC,CAqGvB"}
@@ -0,0 +1,130 @@
1
+ import { Molecule } from 'openchemlib';
2
+ import { ordinal } from "../../format/core/words.js";
3
+ import { isRefinedDuplicate, rankByRefinedEnergy, } from "./conformerRefineRank.js";
4
+ import { conformerShape } from "./conformerShape.js";
5
+ import { centredRmsd, readRelaxableGeometry, writeRelaxedCoordinates, } from "./moleculeCoordinates.js";
6
+ import { toMolfileExport } from "./molfileExport.js";
7
+ /**
8
+ * Energy gap in kcal/mol under which two relaxed conformers can be one minimum.
9
+ *
10
+ * Measured with `benchmark/conformerRefine.js scatter`: one butane minimum
11
+ * reached from six geometries perturbed by up to 0.02 Å lands within 4.9e-6
12
+ * kcal/mol, in 8 to 20 optimizer cycles. So 1e-4 is twenty times the scatter of
13
+ * one minimum, and ten times below the closest gap between two real minima seen
14
+ * on a flexible molecule — ibuprofen has distinct conformers 1e-3 kcal/mol
15
+ * apart. The energy is only the cheap half of the test in any case: what decides
16
+ * that two conformers are one minimum is the shape.
17
+ */
18
+ export const REFINED_SAME_ENERGY_TOLERANCE = 1e-4;
19
+ /** What is shown when a conformer set is ranked by the refined energies. */
20
+ export const REFINED_RANKING = 'refined';
21
+ /**
22
+ * Relax every conformer of a set with a better method and rank the set by what
23
+ * that method says.
24
+ *
25
+ * This is the opt-in second stage of a conformer search, opt-in because it costs
26
+ * seconds where the force field costs milliseconds. MMFF94 places the atoms well
27
+ * but ranks conformers badly — it has no dispersion and no electronic structure,
28
+ * and those are what decide which conformer is lowest. So both numbers are kept:
29
+ * `conformer.energy` stays the force field's, `conformer.refinement.energy` is
30
+ * the new one, and `set.rankedBy` says which of the two the order follows.
31
+ *
32
+ * Refinement changes the set, not only its order: conformers the force field
33
+ * held apart can relax into one minimum, and the first of them is the one that
34
+ * survives. Ids are handed out again over the ranked result, so a caller holding
35
+ * an id must look its conformer up again.
36
+ *
37
+ * A conformer without a force-field energy is refined like any other; its
38
+ * `forceFieldEnergy` is `null` and it takes its place by the refined energy.
39
+ * @param set - The set to refine. Not modified.
40
+ * @param relax - The engine, e.g. `xtbRelaxer` from `react-cheminfo/xtb`.
41
+ * @param options - Method name, duplicate tolerance, progress and cancellation.
42
+ * @returns A new set, ranked by the refined energies.
43
+ * @throws {Error} When the relaxer fails or is cancelled, or answers with the
44
+ * wrong number of geometries.
45
+ */
46
+ export async function refineConformers(set, relax, options = {}) {
47
+ const startedAt = performance.now();
48
+ const method = options.method ?? 'GFN2-xTB';
49
+ if (set.conformers.length === 0) {
50
+ return {
51
+ ...set,
52
+ rankedBy: REFINED_RANKING,
53
+ refinement: { method, elapsedMilliseconds: 0, merged: 0, reordered: 0 },
54
+ };
55
+ }
56
+ const prepared = [];
57
+ for (const conformer of set.conformers) {
58
+ const molecule = Molecule.fromMolfile(conformer.molfile.data);
59
+ prepared.push({
60
+ conformer,
61
+ molecule,
62
+ geometry: readRelaxableGeometry(molecule),
63
+ });
64
+ }
65
+ const relaxed = await relax(prepared.map((entry) => entry.geometry), {
66
+ onSettled: options.onSettled,
67
+ signal: options.signal,
68
+ });
69
+ if (relaxed.length !== set.conformers.length) {
70
+ throw new Error(`The relaxer answered with ${relaxed.length} geometries for ${set.conformers.length} conformers.`);
71
+ }
72
+ const tolerance = options.sameEnergyTolerance ?? REFINED_SAME_ENERGY_TOLERANCE;
73
+ const warnings = [...set.warnings];
74
+ const kept = [];
75
+ const minima = [];
76
+ let merged = 0;
77
+ for (const [index, entry] of prepared.entries()) {
78
+ const result = relaxed[index];
79
+ if (result === undefined)
80
+ continue;
81
+ const { conformer, molecule, geometry } = entry;
82
+ writeRelaxedCoordinates(molecule, result.coordinates);
83
+ const candidate = {
84
+ energy: result.energy,
85
+ shape: conformerShape(molecule),
86
+ };
87
+ if (isRefinedDuplicate(candidate, minima, tolerance)) {
88
+ merged++;
89
+ continue;
90
+ }
91
+ for (const warning of result.warnings) {
92
+ warnings.push(`The ${ordinal(conformer.id)} conformer: ${warning}`);
93
+ }
94
+ if (!result.converged) {
95
+ warnings.push(`The ${ordinal(conformer.id)} conformer did not reach a ${method} minimum in ${result.cycles} cycles, so its refined energy is an upper bound.`);
96
+ }
97
+ kept.push({
98
+ ...conformer,
99
+ molfile: toMolfileExport(molecule),
100
+ refinement: {
101
+ forceFieldId: conformer.id,
102
+ energy: result.energy,
103
+ relativeEnergy: 0,
104
+ dispersionEnergy: result.dispersionEnergy,
105
+ forceFieldEnergy: conformer.energy,
106
+ rmsd: centredRmsd(geometry.coordinates, result.coordinates),
107
+ cycles: result.cycles,
108
+ converged: result.converged,
109
+ },
110
+ });
111
+ minima.push(candidate);
112
+ }
113
+ if (merged > 0) {
114
+ warnings.push(`${merged} conformer(s) relaxed into a ${method} minimum the set already held, so they were dropped.`);
115
+ }
116
+ const ranked = rankByRefinedEnergy(kept);
117
+ return {
118
+ ...set,
119
+ conformers: ranked.conformers,
120
+ warnings,
121
+ rankedBy: REFINED_RANKING,
122
+ refinement: {
123
+ method,
124
+ elapsedMilliseconds: performance.now() - startedAt,
125
+ merged,
126
+ reordered: ranked.reordered,
127
+ },
128
+ };
129
+ }
130
+ //# sourceMappingURL=conformerRefine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformerRefine.js","sourceRoot":"","sources":["../../../src/structure/core/conformerRefine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { ConformerShape } from './conformerShape.ts';
2
+ import type { Conformer } from './conformers.ts';
3
+ /** A refined conformer's energy and shape, which is what tells two minima apart. */
4
+ export interface RefinedMinimum {
5
+ /** Refined total energy, kcal/mol. */
6
+ energy: number;
7
+ shape: ConformerShape;
8
+ }
9
+ /** A ranked set, and how far it moved from the order it came in. */
10
+ export interface RefinedRanking {
11
+ /** The conformers, most stable first, renumbered from one in that order. */
12
+ conformers: Conformer[];
13
+ /** How many conformers ended up at a different position than they came in at. */
14
+ reordered: number;
15
+ }
16
+ /**
17
+ * Order refined conformers from the most stable down, number them from one in
18
+ * that order, and measure every refined energy from the lowest.
19
+ *
20
+ * `reordered` counts the conformers whose position changed, which is the number
21
+ * worth showing: it says how much the better method disagreed with the force
22
+ * field.
23
+ * @param conformers - The refined conformers, in the force field's own order.
24
+ * @returns The ranked conformers and the disagreement count.
25
+ */
26
+ export declare function rankByRefinedEnergy(conformers: readonly Conformer[]): RefinedRanking;
27
+ /**
28
+ * Whether a refined conformer landed in a minimum one of `kept` already holds:
29
+ * the same energy within `tolerance`, and the same shape.
30
+ * @param candidate - The refined conformer's energy and shape.
31
+ * @param kept - The minima kept so far.
32
+ * @param tolerance - Energy gap in kcal/mol under which two energies are one.
33
+ * @returns `true` when one of them is the same minimum.
34
+ */
35
+ export declare function isRefinedDuplicate(candidate: RefinedMinimum, kept: readonly RefinedMinimum[], tolerance: number): boolean;
36
+ //# sourceMappingURL=conformerRefineRank.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformerRefineRank.d.ts","sourceRoot":"","sources":["../../../src/structure/core/conformerRefineRank.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,CAAC;CACvB;AAED,oEAAoE;AACpE,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,SAAS,EAAE,GAC/B,cAAc,CAsBhB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,cAAc,EACzB,IAAI,EAAE,SAAS,cAAc,EAAE,EAC/B,SAAS,EAAE,MAAM,GAChB,OAAO,CAMT"}
@@ -0,0 +1,69 @@
1
+ import { isSameShape } from "./conformerMinimum.js";
2
+ /**
3
+ * Order refined conformers from the most stable down, number them from one in
4
+ * that order, and measure every refined energy from the lowest.
5
+ *
6
+ * `reordered` counts the conformers whose position changed, which is the number
7
+ * worth showing: it says how much the better method disagreed with the force
8
+ * field.
9
+ * @param conformers - The refined conformers, in the force field's own order.
10
+ * @returns The ranked conformers and the disagreement count.
11
+ */
12
+ export function rankByRefinedEnergy(conformers) {
13
+ let lowest = Number.POSITIVE_INFINITY;
14
+ for (const conformer of conformers) {
15
+ const energy = conformer.refinement?.energy;
16
+ if (energy !== undefined && energy < lowest)
17
+ lowest = energy;
18
+ }
19
+ const ranked = conformers.toSorted(compareRefined);
20
+ const result = [];
21
+ let reordered = 0;
22
+ for (const [index, conformer] of ranked.entries()) {
23
+ if (conformer.id !== index + 1)
24
+ reordered++;
25
+ const refinement = conformer.refinement;
26
+ result.push({
27
+ ...conformer,
28
+ id: index + 1,
29
+ refinement: refinement === null
30
+ ? null
31
+ : { ...refinement, relativeEnergy: refinement.energy - lowest },
32
+ });
33
+ }
34
+ return { conformers: result, reordered };
35
+ }
36
+ /**
37
+ * Whether a refined conformer landed in a minimum one of `kept` already holds:
38
+ * the same energy within `tolerance`, and the same shape.
39
+ * @param candidate - The refined conformer's energy and shape.
40
+ * @param kept - The minima kept so far.
41
+ * @param tolerance - Energy gap in kcal/mol under which two energies are one.
42
+ * @returns `true` when one of them is the same minimum.
43
+ */
44
+ export function isRefinedDuplicate(candidate, kept, tolerance) {
45
+ for (const entry of kept) {
46
+ if (Math.abs(candidate.energy - entry.energy) > tolerance)
47
+ continue;
48
+ if (isSameShape(candidate.shape, entry.shape))
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ /**
54
+ * Order two conformers by refined energy; one without a refinement ranks after
55
+ * every one that has one.
56
+ * @param a - One conformer.
57
+ * @param b - The other.
58
+ * @returns Negative when `a` is the more stable, positive when `b` is.
59
+ */
60
+ function compareRefined(a, b) {
61
+ const energyA = a.refinement?.energy;
62
+ const energyB = b.refinement?.energy;
63
+ if (energyA === undefined)
64
+ return energyB === undefined ? 0 : 1;
65
+ if (energyB === undefined)
66
+ return -1;
67
+ return energyA - energyB;
68
+ }
69
+ //# sourceMappingURL=conformerRefineRank.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformerRefineRank.js","sourceRoot":"","sources":["../../../src/structure/core/conformerRefineRank.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAmBpD;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAAgC;IAEhC,IAAI,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACtC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,GAAG,MAAM;YAAE,MAAM,GAAG,MAAM,CAAC;IAC/D,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACnD,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAClD,IAAI,SAAS,CAAC,EAAE,KAAK,KAAK,GAAG,CAAC;YAAE,SAAS,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC;YACV,GAAG,SAAS;YACZ,EAAE,EAAE,KAAK,GAAG,CAAC;YACb,UAAU,EACR,UAAU,KAAK,IAAI;gBACjB,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,cAAc,EAAE,UAAU,CAAC,MAAM,GAAG,MAAM,EAAE;SACpE,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAyB,EACzB,IAA+B,EAC/B,SAAiB;IAEjB,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS;YAAE,SAAS;QACpE,IAAI,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;IAC7D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,CAAY,EAAE,CAAY;IAChD,MAAM,OAAO,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC;IACrC,MAAM,OAAO,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC;IACrC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC,CAAC;IACrC,OAAO,OAAO,GAAG,OAAO,CAAC;AAC3B,CAAC"}