mlmm-toolkit 0.2.2.dev0__py3-none-any.whl

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 (372) hide show
  1. hessian_ff/__init__.py +50 -0
  2. hessian_ff/analytical_hessian.py +609 -0
  3. hessian_ff/constants.py +46 -0
  4. hessian_ff/forcefield.py +339 -0
  5. hessian_ff/loaders.py +608 -0
  6. hessian_ff/native/Makefile +8 -0
  7. hessian_ff/native/__init__.py +28 -0
  8. hessian_ff/native/analytical_hessian.py +88 -0
  9. hessian_ff/native/analytical_hessian_ext.cpp +258 -0
  10. hessian_ff/native/bonded.py +82 -0
  11. hessian_ff/native/bonded_ext.cpp +640 -0
  12. hessian_ff/native/loader.py +349 -0
  13. hessian_ff/native/nonbonded.py +118 -0
  14. hessian_ff/native/nonbonded_ext.cpp +1150 -0
  15. hessian_ff/prmtop_parmed.py +23 -0
  16. hessian_ff/system.py +107 -0
  17. hessian_ff/terms/__init__.py +14 -0
  18. hessian_ff/terms/angle.py +73 -0
  19. hessian_ff/terms/bond.py +44 -0
  20. hessian_ff/terms/cmap.py +406 -0
  21. hessian_ff/terms/dihedral.py +141 -0
  22. hessian_ff/terms/nonbonded.py +209 -0
  23. hessian_ff/tests/__init__.py +0 -0
  24. hessian_ff/tests/conftest.py +75 -0
  25. hessian_ff/tests/data/small/complex.parm7 +1346 -0
  26. hessian_ff/tests/data/small/complex.pdb +125 -0
  27. hessian_ff/tests/data/small/complex.rst7 +63 -0
  28. hessian_ff/tests/test_coords_input.py +44 -0
  29. hessian_ff/tests/test_energy_force.py +49 -0
  30. hessian_ff/tests/test_hessian.py +137 -0
  31. hessian_ff/tests/test_smoke.py +18 -0
  32. hessian_ff/tests/test_validation.py +40 -0
  33. hessian_ff/workflows.py +889 -0
  34. mlmm/__init__.py +36 -0
  35. mlmm/__main__.py +7 -0
  36. mlmm/_version.py +34 -0
  37. mlmm/add_elem_info.py +374 -0
  38. mlmm/advanced_help.py +91 -0
  39. mlmm/align_freeze_atoms.py +601 -0
  40. mlmm/all.py +3535 -0
  41. mlmm/bond_changes.py +231 -0
  42. mlmm/bool_compat.py +223 -0
  43. mlmm/cli.py +574 -0
  44. mlmm/cli_utils.py +166 -0
  45. mlmm/default_group.py +337 -0
  46. mlmm/defaults.py +467 -0
  47. mlmm/define_layer.py +526 -0
  48. mlmm/dft.py +1041 -0
  49. mlmm/energy_diagram.py +253 -0
  50. mlmm/extract.py +2213 -0
  51. mlmm/fix_altloc.py +464 -0
  52. mlmm/freq.py +1406 -0
  53. mlmm/harmonic_constraints.py +140 -0
  54. mlmm/hessian_cache.py +44 -0
  55. mlmm/hessian_calc.py +174 -0
  56. mlmm/irc.py +638 -0
  57. mlmm/mlmm_calc.py +2262 -0
  58. mlmm/mm_parm.py +945 -0
  59. mlmm/oniom_export.py +1983 -0
  60. mlmm/oniom_import.py +457 -0
  61. mlmm/opt.py +1742 -0
  62. mlmm/path_opt.py +1353 -0
  63. mlmm/path_search.py +2299 -0
  64. mlmm/preflight.py +88 -0
  65. mlmm/py.typed +1 -0
  66. mlmm/pysis_runner.py +45 -0
  67. mlmm/scan.py +1047 -0
  68. mlmm/scan2d.py +1226 -0
  69. mlmm/scan3d.py +1265 -0
  70. mlmm/scan_common.py +184 -0
  71. mlmm/summary_log.py +736 -0
  72. mlmm/trj2fig.py +448 -0
  73. mlmm/tsopt.py +2871 -0
  74. mlmm/utils.py +2309 -0
  75. mlmm/xtb_embedcharge_correction.py +475 -0
  76. mlmm_toolkit-0.2.2.dev0.dist-info/METADATA +1159 -0
  77. mlmm_toolkit-0.2.2.dev0.dist-info/RECORD +372 -0
  78. mlmm_toolkit-0.2.2.dev0.dist-info/WHEEL +5 -0
  79. mlmm_toolkit-0.2.2.dev0.dist-info/entry_points.txt +2 -0
  80. mlmm_toolkit-0.2.2.dev0.dist-info/licenses/LICENSE +674 -0
  81. mlmm_toolkit-0.2.2.dev0.dist-info/top_level.txt +4 -0
  82. pysisyphus/Geometry.py +1667 -0
  83. pysisyphus/LICENSE +674 -0
  84. pysisyphus/TableFormatter.py +63 -0
  85. pysisyphus/TablePrinter.py +74 -0
  86. pysisyphus/__init__.py +12 -0
  87. pysisyphus/calculators/AFIR.py +452 -0
  88. pysisyphus/calculators/AnaPot.py +20 -0
  89. pysisyphus/calculators/AnaPot2.py +48 -0
  90. pysisyphus/calculators/AnaPot3.py +12 -0
  91. pysisyphus/calculators/AnaPot4.py +20 -0
  92. pysisyphus/calculators/AnaPotBase.py +337 -0
  93. pysisyphus/calculators/AnaPotCBM.py +25 -0
  94. pysisyphus/calculators/AtomAtomTransTorque.py +154 -0
  95. pysisyphus/calculators/CFOUR.py +250 -0
  96. pysisyphus/calculators/Calculator.py +844 -0
  97. pysisyphus/calculators/CerjanMiller.py +24 -0
  98. pysisyphus/calculators/Composite.py +123 -0
  99. pysisyphus/calculators/ConicalIntersection.py +171 -0
  100. pysisyphus/calculators/DFTBp.py +430 -0
  101. pysisyphus/calculators/DFTD3.py +66 -0
  102. pysisyphus/calculators/DFTD4.py +84 -0
  103. pysisyphus/calculators/Dalton.py +61 -0
  104. pysisyphus/calculators/Dimer.py +681 -0
  105. pysisyphus/calculators/Dummy.py +20 -0
  106. pysisyphus/calculators/EGO.py +76 -0
  107. pysisyphus/calculators/EnergyMin.py +224 -0
  108. pysisyphus/calculators/ExternalPotential.py +264 -0
  109. pysisyphus/calculators/FakeASE.py +35 -0
  110. pysisyphus/calculators/FourWellAnaPot.py +28 -0
  111. pysisyphus/calculators/FreeEndNEBPot.py +39 -0
  112. pysisyphus/calculators/Gaussian09.py +18 -0
  113. pysisyphus/calculators/Gaussian16.py +726 -0
  114. pysisyphus/calculators/HardSphere.py +159 -0
  115. pysisyphus/calculators/IDPPCalculator.py +49 -0
  116. pysisyphus/calculators/IPIClient.py +133 -0
  117. pysisyphus/calculators/IPIServer.py +234 -0
  118. pysisyphus/calculators/LEPSBase.py +24 -0
  119. pysisyphus/calculators/LEPSExpr.py +139 -0
  120. pysisyphus/calculators/LennardJones.py +80 -0
  121. pysisyphus/calculators/MOPAC.py +219 -0
  122. pysisyphus/calculators/MullerBrownSympyPot.py +51 -0
  123. pysisyphus/calculators/MultiCalc.py +85 -0
  124. pysisyphus/calculators/NFK.py +45 -0
  125. pysisyphus/calculators/OBabel.py +87 -0
  126. pysisyphus/calculators/ONIOMv2.py +1129 -0
  127. pysisyphus/calculators/ORCA.py +893 -0
  128. pysisyphus/calculators/ORCA5.py +6 -0
  129. pysisyphus/calculators/OpenMM.py +88 -0
  130. pysisyphus/calculators/OpenMolcas.py +281 -0
  131. pysisyphus/calculators/OverlapCalculator.py +908 -0
  132. pysisyphus/calculators/Psi4.py +218 -0
  133. pysisyphus/calculators/PyPsi4.py +37 -0
  134. pysisyphus/calculators/PySCF.py +341 -0
  135. pysisyphus/calculators/PyXTB.py +73 -0
  136. pysisyphus/calculators/QCEngine.py +106 -0
  137. pysisyphus/calculators/Rastrigin.py +22 -0
  138. pysisyphus/calculators/Remote.py +76 -0
  139. pysisyphus/calculators/Rosenbrock.py +15 -0
  140. pysisyphus/calculators/SocketCalc.py +97 -0
  141. pysisyphus/calculators/TIP3P.py +111 -0
  142. pysisyphus/calculators/TransTorque.py +161 -0
  143. pysisyphus/calculators/Turbomole.py +965 -0
  144. pysisyphus/calculators/VRIPot.py +37 -0
  145. pysisyphus/calculators/WFOWrapper.py +333 -0
  146. pysisyphus/calculators/WFOWrapper2.py +341 -0
  147. pysisyphus/calculators/XTB.py +418 -0
  148. pysisyphus/calculators/__init__.py +81 -0
  149. pysisyphus/calculators/cosmo_data.py +139 -0
  150. pysisyphus/calculators/parser.py +150 -0
  151. pysisyphus/color.py +19 -0
  152. pysisyphus/config.py +133 -0
  153. pysisyphus/constants.py +65 -0
  154. pysisyphus/cos/AdaptiveNEB.py +230 -0
  155. pysisyphus/cos/ChainOfStates.py +725 -0
  156. pysisyphus/cos/FreeEndNEB.py +25 -0
  157. pysisyphus/cos/FreezingString.py +103 -0
  158. pysisyphus/cos/GrowingChainOfStates.py +71 -0
  159. pysisyphus/cos/GrowingNT.py +309 -0
  160. pysisyphus/cos/GrowingString.py +508 -0
  161. pysisyphus/cos/NEB.py +189 -0
  162. pysisyphus/cos/SimpleZTS.py +64 -0
  163. pysisyphus/cos/__init__.py +22 -0
  164. pysisyphus/cos/stiffness.py +199 -0
  165. pysisyphus/drivers/__init__.py +17 -0
  166. pysisyphus/drivers/afir.py +855 -0
  167. pysisyphus/drivers/barriers.py +271 -0
  168. pysisyphus/drivers/birkholz.py +138 -0
  169. pysisyphus/drivers/cluster.py +318 -0
  170. pysisyphus/drivers/diabatization.py +133 -0
  171. pysisyphus/drivers/merge.py +368 -0
  172. pysisyphus/drivers/merge_mol2.py +322 -0
  173. pysisyphus/drivers/opt.py +375 -0
  174. pysisyphus/drivers/perf.py +91 -0
  175. pysisyphus/drivers/pka.py +52 -0
  176. pysisyphus/drivers/precon_pos_rot.py +669 -0
  177. pysisyphus/drivers/rates.py +480 -0
  178. pysisyphus/drivers/replace.py +219 -0
  179. pysisyphus/drivers/scan.py +212 -0
  180. pysisyphus/drivers/spectrum.py +166 -0
  181. pysisyphus/drivers/thermo.py +31 -0
  182. pysisyphus/dynamics/Gaussian.py +103 -0
  183. pysisyphus/dynamics/__init__.py +20 -0
  184. pysisyphus/dynamics/colvars.py +136 -0
  185. pysisyphus/dynamics/driver.py +297 -0
  186. pysisyphus/dynamics/helpers.py +256 -0
  187. pysisyphus/dynamics/lincs.py +105 -0
  188. pysisyphus/dynamics/mdp.py +364 -0
  189. pysisyphus/dynamics/rattle.py +121 -0
  190. pysisyphus/dynamics/thermostats.py +128 -0
  191. pysisyphus/dynamics/wigner.py +266 -0
  192. pysisyphus/elem_data.py +3473 -0
  193. pysisyphus/exceptions.py +2 -0
  194. pysisyphus/filtertrj.py +69 -0
  195. pysisyphus/helpers.py +623 -0
  196. pysisyphus/helpers_pure.py +649 -0
  197. pysisyphus/init_logging.py +50 -0
  198. pysisyphus/intcoords/Bend.py +69 -0
  199. pysisyphus/intcoords/Bend2.py +25 -0
  200. pysisyphus/intcoords/BondedFragment.py +32 -0
  201. pysisyphus/intcoords/Cartesian.py +41 -0
  202. pysisyphus/intcoords/CartesianCoords.py +140 -0
  203. pysisyphus/intcoords/Coords.py +56 -0
  204. pysisyphus/intcoords/DLC.py +197 -0
  205. pysisyphus/intcoords/DistanceFunction.py +34 -0
  206. pysisyphus/intcoords/DummyImproper.py +70 -0
  207. pysisyphus/intcoords/DummyTorsion.py +72 -0
  208. pysisyphus/intcoords/LinearBend.py +105 -0
  209. pysisyphus/intcoords/LinearDisplacement.py +80 -0
  210. pysisyphus/intcoords/OutOfPlane.py +59 -0
  211. pysisyphus/intcoords/PrimTypes.py +286 -0
  212. pysisyphus/intcoords/Primitive.py +137 -0
  213. pysisyphus/intcoords/RedundantCoords.py +659 -0
  214. pysisyphus/intcoords/RobustTorsion.py +59 -0
  215. pysisyphus/intcoords/Rotation.py +147 -0
  216. pysisyphus/intcoords/Stretch.py +31 -0
  217. pysisyphus/intcoords/Torsion.py +101 -0
  218. pysisyphus/intcoords/Torsion2.py +25 -0
  219. pysisyphus/intcoords/Translation.py +45 -0
  220. pysisyphus/intcoords/__init__.py +61 -0
  221. pysisyphus/intcoords/augment_bonds.py +126 -0
  222. pysisyphus/intcoords/derivatives.py +10512 -0
  223. pysisyphus/intcoords/eval.py +80 -0
  224. pysisyphus/intcoords/exceptions.py +37 -0
  225. pysisyphus/intcoords/findiffs.py +48 -0
  226. pysisyphus/intcoords/generate_derivatives.py +414 -0
  227. pysisyphus/intcoords/helpers.py +235 -0
  228. pysisyphus/intcoords/logging_conf.py +10 -0
  229. pysisyphus/intcoords/mp_derivatives.py +10836 -0
  230. pysisyphus/intcoords/setup.py +962 -0
  231. pysisyphus/intcoords/setup_fast.py +176 -0
  232. pysisyphus/intcoords/update.py +272 -0
  233. pysisyphus/intcoords/valid.py +89 -0
  234. pysisyphus/interpolate/Geodesic.py +93 -0
  235. pysisyphus/interpolate/IDPP.py +55 -0
  236. pysisyphus/interpolate/Interpolator.py +116 -0
  237. pysisyphus/interpolate/LST.py +70 -0
  238. pysisyphus/interpolate/Redund.py +152 -0
  239. pysisyphus/interpolate/__init__.py +9 -0
  240. pysisyphus/interpolate/helpers.py +34 -0
  241. pysisyphus/io/__init__.py +22 -0
  242. pysisyphus/io/aomix.py +178 -0
  243. pysisyphus/io/cjson.py +24 -0
  244. pysisyphus/io/crd.py +101 -0
  245. pysisyphus/io/cube.py +220 -0
  246. pysisyphus/io/fchk.py +184 -0
  247. pysisyphus/io/hdf5.py +49 -0
  248. pysisyphus/io/hessian.py +72 -0
  249. pysisyphus/io/mol2.py +146 -0
  250. pysisyphus/io/molden.py +293 -0
  251. pysisyphus/io/orca.py +189 -0
  252. pysisyphus/io/pdb.py +269 -0
  253. pysisyphus/io/psf.py +79 -0
  254. pysisyphus/io/pubchem.py +31 -0
  255. pysisyphus/io/qcschema.py +34 -0
  256. pysisyphus/io/sdf.py +29 -0
  257. pysisyphus/io/xyz.py +61 -0
  258. pysisyphus/io/zmat.py +175 -0
  259. pysisyphus/irc/DWI.py +108 -0
  260. pysisyphus/irc/DampedVelocityVerlet.py +134 -0
  261. pysisyphus/irc/Euler.py +22 -0
  262. pysisyphus/irc/EulerPC.py +345 -0
  263. pysisyphus/irc/GonzalezSchlegel.py +187 -0
  264. pysisyphus/irc/IMKMod.py +164 -0
  265. pysisyphus/irc/IRC.py +878 -0
  266. pysisyphus/irc/IRCDummy.py +10 -0
  267. pysisyphus/irc/Instanton.py +307 -0
  268. pysisyphus/irc/LQA.py +53 -0
  269. pysisyphus/irc/ModeKill.py +136 -0
  270. pysisyphus/irc/ParamPlot.py +53 -0
  271. pysisyphus/irc/RK4.py +36 -0
  272. pysisyphus/irc/__init__.py +31 -0
  273. pysisyphus/irc/initial_displ.py +219 -0
  274. pysisyphus/linalg.py +411 -0
  275. pysisyphus/line_searches/Backtracking.py +88 -0
  276. pysisyphus/line_searches/HagerZhang.py +184 -0
  277. pysisyphus/line_searches/LineSearch.py +232 -0
  278. pysisyphus/line_searches/StrongWolfe.py +108 -0
  279. pysisyphus/line_searches/__init__.py +9 -0
  280. pysisyphus/line_searches/interpol.py +15 -0
  281. pysisyphus/modefollow/NormalMode.py +40 -0
  282. pysisyphus/modefollow/__init__.py +10 -0
  283. pysisyphus/modefollow/davidson.py +199 -0
  284. pysisyphus/modefollow/lanczos.py +95 -0
  285. pysisyphus/optimizers/BFGS.py +99 -0
  286. pysisyphus/optimizers/BacktrackingOptimizer.py +113 -0
  287. pysisyphus/optimizers/ConjugateGradient.py +98 -0
  288. pysisyphus/optimizers/CubicNewton.py +75 -0
  289. pysisyphus/optimizers/FIRE.py +113 -0
  290. pysisyphus/optimizers/HessianOptimizer.py +1176 -0
  291. pysisyphus/optimizers/LBFGS.py +228 -0
  292. pysisyphus/optimizers/LayerOpt.py +411 -0
  293. pysisyphus/optimizers/MicroOptimizer.py +169 -0
  294. pysisyphus/optimizers/NCOptimizer.py +90 -0
  295. pysisyphus/optimizers/Optimizer.py +1084 -0
  296. pysisyphus/optimizers/PreconLBFGS.py +260 -0
  297. pysisyphus/optimizers/PreconSteepestDescent.py +7 -0
  298. pysisyphus/optimizers/QuickMin.py +74 -0
  299. pysisyphus/optimizers/RFOptimizer.py +181 -0
  300. pysisyphus/optimizers/RSA.py +99 -0
  301. pysisyphus/optimizers/StabilizedQNMethod.py +248 -0
  302. pysisyphus/optimizers/SteepestDescent.py +23 -0
  303. pysisyphus/optimizers/StringOptimizer.py +173 -0
  304. pysisyphus/optimizers/__init__.py +41 -0
  305. pysisyphus/optimizers/closures.py +301 -0
  306. pysisyphus/optimizers/cls_map.py +58 -0
  307. pysisyphus/optimizers/exceptions.py +6 -0
  308. pysisyphus/optimizers/gdiis.py +280 -0
  309. pysisyphus/optimizers/guess_hessians.py +311 -0
  310. pysisyphus/optimizers/hessian_updates.py +355 -0
  311. pysisyphus/optimizers/poly_fit.py +285 -0
  312. pysisyphus/optimizers/precon.py +153 -0
  313. pysisyphus/optimizers/restrict_step.py +24 -0
  314. pysisyphus/pack.py +172 -0
  315. pysisyphus/peakdetect.py +948 -0
  316. pysisyphus/plot.py +1031 -0
  317. pysisyphus/run.py +2106 -0
  318. pysisyphus/socket_helper.py +74 -0
  319. pysisyphus/stocastic/FragmentKick.py +132 -0
  320. pysisyphus/stocastic/Kick.py +81 -0
  321. pysisyphus/stocastic/Pipeline.py +303 -0
  322. pysisyphus/stocastic/__init__.py +21 -0
  323. pysisyphus/stocastic/align.py +127 -0
  324. pysisyphus/testing.py +96 -0
  325. pysisyphus/thermo.py +156 -0
  326. pysisyphus/trj.py +824 -0
  327. pysisyphus/tsoptimizers/RSIRFOptimizer.py +56 -0
  328. pysisyphus/tsoptimizers/RSPRFOptimizer.py +182 -0
  329. pysisyphus/tsoptimizers/TRIM.py +59 -0
  330. pysisyphus/tsoptimizers/TSHessianOptimizer.py +463 -0
  331. pysisyphus/tsoptimizers/__init__.py +23 -0
  332. pysisyphus/wavefunction/Basis.py +239 -0
  333. pysisyphus/wavefunction/DIIS.py +76 -0
  334. pysisyphus/wavefunction/__init__.py +25 -0
  335. pysisyphus/wavefunction/build_ext.py +42 -0
  336. pysisyphus/wavefunction/cart2sph.py +190 -0
  337. pysisyphus/wavefunction/diabatization.py +304 -0
  338. pysisyphus/wavefunction/excited_states.py +435 -0
  339. pysisyphus/wavefunction/gen_ints.py +1811 -0
  340. pysisyphus/wavefunction/helpers.py +104 -0
  341. pysisyphus/wavefunction/ints/__init__.py +0 -0
  342. pysisyphus/wavefunction/ints/boys.py +193 -0
  343. pysisyphus/wavefunction/ints/boys_table_N_64_xasym_27.1_step_0.01.npy +0 -0
  344. pysisyphus/wavefunction/ints/cart_gto3d.py +176 -0
  345. pysisyphus/wavefunction/ints/coulomb3d.py +25928 -0
  346. pysisyphus/wavefunction/ints/diag_quadrupole3d.py +10036 -0
  347. pysisyphus/wavefunction/ints/dipole3d.py +8762 -0
  348. pysisyphus/wavefunction/ints/int2c2e3d.py +7198 -0
  349. pysisyphus/wavefunction/ints/int3c2e3d_sph.py +65040 -0
  350. pysisyphus/wavefunction/ints/kinetic3d.py +8240 -0
  351. pysisyphus/wavefunction/ints/ovlp3d.py +3777 -0
  352. pysisyphus/wavefunction/ints/quadrupole3d.py +15054 -0
  353. pysisyphus/wavefunction/ints/self_ovlp3d.py +198 -0
  354. pysisyphus/wavefunction/localization.py +458 -0
  355. pysisyphus/wavefunction/multipole.py +159 -0
  356. pysisyphus/wavefunction/normalization.py +36 -0
  357. pysisyphus/wavefunction/pop_analysis.py +134 -0
  358. pysisyphus/wavefunction/shells.py +1171 -0
  359. pysisyphus/wavefunction/wavefunction.py +504 -0
  360. pysisyphus/wrapper/__init__.py +11 -0
  361. pysisyphus/wrapper/exceptions.py +2 -0
  362. pysisyphus/wrapper/jmol.py +120 -0
  363. pysisyphus/wrapper/mwfn.py +169 -0
  364. pysisyphus/wrapper/packmol.py +71 -0
  365. pysisyphus/xyzloader.py +168 -0
  366. pysisyphus/yaml_mods.py +45 -0
  367. thermoanalysis/LICENSE +674 -0
  368. thermoanalysis/QCData.py +244 -0
  369. thermoanalysis/__init__.py +0 -0
  370. thermoanalysis/config.py +3 -0
  371. thermoanalysis/constants.py +20 -0
  372. thermoanalysis/thermo.py +1011 -0
@@ -0,0 +1,176 @@
1
+ import itertools as it
2
+ import logging
3
+
4
+ import numpy as np
5
+ from sklearn.neighbors import KDTree
6
+
7
+ from pysisyphus.elem_data import COVALENT_RADII as CR
8
+ from pysisyphus.helpers_pure import log, timed
9
+ from pysisyphus.intcoords.valid import bend_valid, dihedral_valid
10
+
11
+
12
+ logger = logging.getLogger("internal_coords")
13
+
14
+ BOND_FACTOR = 1.3
15
+
16
+
17
+ def get_max_bond_dists(atoms, bond_factor, covalent_radii=None):
18
+ if covalent_radii is None:
19
+ cr = CR
20
+ else:
21
+ cr = {atom: covrad for atom, covrad in zip(atoms, covalent_radii)}
22
+
23
+ unique_atoms = set(atoms)
24
+ atom_pairs = it.combinations_with_replacement(unique_atoms, 2)
25
+ max_bond_dists = {
26
+ frozenset((from_, to_)): bond_factor * (cr[from_] + cr[to_])
27
+ for from_, to_ in atom_pairs
28
+ }
29
+ return max_bond_dists
30
+
31
+
32
+ def find_bonds(
33
+ atoms, coords3d, covalent_radii=None, bond_factor=BOND_FACTOR, min_dist=0.1
34
+ ):
35
+ atoms = [atom.lower() for atom in atoms]
36
+ c3d = coords3d.reshape(-1, 3)
37
+ if covalent_radii is None:
38
+ covalent_radii = [CR[atom] for atom in atoms]
39
+ cr = np.array(covalent_radii)
40
+
41
+ max_bond_dists = get_max_bond_dists(atoms, bond_factor, covalent_radii=cr)
42
+ radii = bond_factor * (cr.copy() + max(cr))
43
+ kdt = KDTree(c3d)
44
+ res, dists = kdt.query_radius(c3d, radii, return_distance=True)
45
+ bonds_ = list()
46
+ for i, (atom, bonds, dists_) in enumerate(zip(atoms, res, dists)):
47
+ fsets = [frozenset((atom, atoms[a])) for a in bonds]
48
+ ref_dists = np.array([max_bond_dists[fset] for fset in fsets])
49
+ keep = np.logical_and(dists_ <= ref_dists, min_dist <= dists_)
50
+ for to_ in bonds[keep]:
51
+ bonds_.append(frozenset((i, to_)))
52
+ bonds = np.array([tuple((from_, to_)) for from_, to_ in set(bonds_)])
53
+ return bonds
54
+
55
+
56
+ def find_bonds_for_geom(geom, bond_factor=BOND_FACTOR):
57
+ return find_bonds(geom.atoms, geom.coords3d, geom.covalent_radii)
58
+
59
+
60
+ def get_bond_vec_getter(
61
+ atoms, covalent_radii, bonds_for_inds, no_bonds_with=None, bond_factor=BOND_FACTOR
62
+ ):
63
+ max_bond_dists = get_max_bond_dists(
64
+ atoms, bond_factor, covalent_radii=covalent_radii
65
+ )
66
+ max_bond_dists_for_inds = [
67
+ np.array([max_bond_dists[frozenset((atoms[ind], atom_))] for atom_ in atoms])
68
+ for ind in bonds_for_inds
69
+ ]
70
+ if no_bonds_with is None:
71
+ # List of empty lists
72
+ no_bonds_with = [[] * len(bonds_for_inds)]
73
+
74
+ # Set it to a negative value, so the calculated distance, which is always positive
75
+ # can't be smaller than this value.
76
+ for mbd, nbw in zip(max_bond_dists_for_inds, no_bonds_with):
77
+ mbd[nbw] = -1
78
+
79
+ all_inds = np.arange(len(atoms))
80
+
81
+ def get_bond_vecs(coords, return_bonded_inds=False):
82
+ coords3d = coords.reshape(-1, 3)
83
+ all_bond_vecs = list()
84
+ all_bonded_inds = list()
85
+ for ind, max_dists in zip(bonds_for_inds, max_bond_dists_for_inds):
86
+ distance_vecs = coords3d - coords3d[ind]
87
+ distances = np.linalg.norm(distance_vecs, axis=1)
88
+ # Set 0.0 distance of atom with itself to a high value to not form
89
+ # and ind-ind bond.
90
+ distances[ind] = 10_000
91
+ bond_mask = distances <= max_dists
92
+ all_bond_vecs.append(distance_vecs[bond_mask])
93
+ all_bonded_inds.append(all_inds[bond_mask])
94
+
95
+ if return_bonded_inds:
96
+ return all_bond_vecs, all_bonded_inds
97
+ else:
98
+ return all_bond_vecs
99
+
100
+ return get_bond_vecs
101
+
102
+
103
+ def get_bend_candidates(bonds):
104
+ """Also yields duplicates [a, b, c] and [c, b, a]."""
105
+ bond_dict = {}
106
+ bonds = [tuple(bond) for bond in bonds]
107
+ # Construct a dictionary holding neighbours for a given atom.
108
+ for from_, to_ in bonds:
109
+ bond_dict.setdefault(from_, list()).append(to_)
110
+ bond_dict.setdefault(to_, list()).append(from_)
111
+
112
+ for bond in bonds:
113
+ from_, to_ = bond
114
+ # Look up neighbours of from_ and to_ in the dictionary
115
+ from_neighs = set(bond_dict[from_]) - set((to_,))
116
+ to_neighs = set(bond_dict[to_]) - set((from_,))
117
+ bend_candidates = [(neigh,) + bond for neigh in from_neighs] + [
118
+ bond + (neigh,) for neigh in to_neighs
119
+ ]
120
+ yield from bend_candidates
121
+
122
+
123
+ def find_bends(coords3d, bonds, min_deg, max_deg, logger=None):
124
+ bend_set = set()
125
+ for indices in get_bend_candidates(bonds):
126
+ if (
127
+ (not bend_valid(coords3d, indices, min_deg, max_deg))
128
+ or (indices in bend_set)
129
+ or (indices[::-1] in bend_set)
130
+ ):
131
+ continue
132
+ bend_set.add(indices)
133
+ return [list(bend) for bend in bend_set]
134
+
135
+
136
+ def find_dihedrals(coords3d, bonds, bends, max_deg, logger=None):
137
+ bond_dict = {}
138
+ bonds = [tuple(bond) for bond in bonds]
139
+ for from_, to_ in bonds:
140
+ bond_dict.setdefault(from_, list()).append(to_)
141
+ bond_dict.setdefault(to_, list()).append(from_)
142
+
143
+ dihedral_set = set()
144
+ for bend in bends:
145
+ bend = tuple(bend)
146
+ from_, central, to_ = bend
147
+ from_neighs = set(bond_dict[from_]) - set((to_, central))
148
+ to_neighs = set(bond_dict[to_]) - set((from_, central))
149
+ dihedral_candidates = [(neigh,) + bend for neigh in from_neighs] + [
150
+ bend + (neigh,) for neigh in to_neighs
151
+ ]
152
+ for indices in dihedral_candidates:
153
+ if not dihedral_valid(coords3d, indices, deg_thresh=max_deg):
154
+ continue
155
+ dihedral_set.add(indices)
156
+ return [list(dihedral) for dihedral in dihedral_set]
157
+
158
+
159
+ def find_bonds_bends(geom, bond_factor=BOND_FACTOR, min_deg=15, max_deg=175):
160
+ log(logger, "Starting detection of bonds and bends.")
161
+ bonds = find_bonds_for_geom(geom, bond_factor=bond_factor)
162
+ log(logger, f"Found {len(bonds)} bonds.")
163
+ bends = find_bends(geom.coords3d, bonds, min_deg=min_deg, max_deg=max_deg)
164
+ log(logger, f"Found {len(bends)} bends.")
165
+ return bonds, bends
166
+
167
+
168
+ @timed(logger)
169
+ def find_bonds_bends_dihedrals(geom, bond_factor=BOND_FACTOR, min_deg=15, max_deg=175):
170
+ log(logger, f"Detecting bonds, bends and dihedrals for {len(geom.atoms)} atoms.")
171
+ bonds, bends = find_bonds_bends(
172
+ geom, bond_factor=bond_factor, min_deg=min_deg, max_deg=max_deg
173
+ )
174
+ proper_dihedrals = find_dihedrals(geom.coords3d, bonds, bends, max_deg)
175
+ log(logger, f"Found {len(proper_dihedrals)} proper dihedrals.")
176
+ return bonds, bends, proper_dihedrals
@@ -0,0 +1,272 @@
1
+ import numpy as np
2
+
3
+ from pysisyphus.config import BEND_MIN_DEG, LB_MIN_DEG
4
+ from pysisyphus.helpers_pure import log
5
+ from pysisyphus.intcoords.eval import eval_primitives
6
+ from pysisyphus.intcoords.exceptions import NeedNewInternalsException
7
+ from pysisyphus.intcoords.valid import bend_valid, dihedral_valid
8
+ from pysisyphus.intcoords.PrimTypes import Bends, Dihedrals, Rotations
9
+
10
+
11
+ def correct_dihedrals(new_dihedrals, old_dihedrals):
12
+ """Dihedrals are periodic. Going from -179° to 179° is not a step of 358°,
13
+ but a step of 2°. By considering the actual distance of the dihedrals from
14
+ π the correct step can be calculated.
15
+
16
+ dihedral step length = abs(abs(new_dihedral) - π) + abs(abs(old_dihedral) - π)
17
+
18
+ or put differently
19
+
20
+ dihedral step length = abs(abs(new_dihedral - old_dihedral) - 2*π)
21
+
22
+ The sign is left to be determined. Going from -179° to 179° (roughly π - -π = 2π)
23
+ is a counter clockwise rotation and the dihedral has to decrease below -π. Going
24
+ from 179° to -179° (roughly -π - π = -2π) is a clockwise rotation and the dihedral
25
+ increases abvove π. So the correct sign corresponds to the negative sign of the
26
+ original difference.
27
+
28
+ original difference 2π -> dihedral must decrease -> sign = -1
29
+ original difference -2π -> dihedral must increase -> sign = +1
30
+
31
+ Overall, the old dihedral is modified by the actual step length with the correct
32
+ sign."""
33
+ new_dihedrals = np.atleast_1d(new_dihedrals)
34
+ old_dihedrals = np.atleast_1d(old_dihedrals)
35
+ dihedrals_step = new_dihedrals - old_dihedrals
36
+ shifted_by_2pi = np.abs(np.abs(dihedrals_step) - 2 * np.pi) < np.pi / 2
37
+ corrected_dihedrals = new_dihedrals.copy()
38
+ corrected_dihedrals[shifted_by_2pi] -= (
39
+ 2 * np.pi * np.sign(dihedrals_step[shifted_by_2pi])
40
+ )
41
+ return corrected_dihedrals
42
+
43
+
44
+ def update_internals(
45
+ new_coords3d,
46
+ old_internals,
47
+ primitives,
48
+ dihedral_inds,
49
+ rotation_inds,
50
+ bend_inds,
51
+ check_dihedrals=False,
52
+ check_bends=False,
53
+ bend_min_deg=BEND_MIN_DEG,
54
+ bend_max_deg=LB_MIN_DEG,
55
+ rotation_thresh=0.9,
56
+ logger=None,
57
+ ):
58
+ prim_internals = eval_primitives(new_coords3d, primitives)
59
+ new_internals = np.array([prim_int.val for prim_int in prim_internals])
60
+ internal_diffs = new_internals - old_internals
61
+
62
+ new_rotations = new_internals[rotation_inds]
63
+ # Check for approaching singularity as discussed in the geomeTRIC paper. The
64
+ # original code seems to check this only for linear molecules and instead
65
+ # does some +2π/-2π magic, similar to how dihedrals differences are handled.
66
+ if (np.abs(new_rotations / np.pi) >= rotation_thresh).any():
67
+ raise NeedNewInternalsException(new_coords3d)
68
+
69
+ dihedrals = [prim_internals[i] for i in dihedral_inds]
70
+
71
+ dihedral_diffs = internal_diffs[dihedral_inds]
72
+
73
+ # Find differences that are shifted by 2*pi
74
+ shifted_by_2pi = np.abs(np.abs(dihedral_diffs) - 2 * np.pi) < np.pi / 2
75
+ new_dihedrals = np.array([dihed.val for dihed in dihedrals])
76
+ if any(shifted_by_2pi):
77
+ new_dihedrals[shifted_by_2pi] -= (
78
+ 2 * np.pi * np.sign(dihedral_diffs[shifted_by_2pi])
79
+ )
80
+ # Update values
81
+ for dihed, new_val in zip(dihedrals, new_dihedrals):
82
+ dihed.val = new_val
83
+
84
+ invalid_inds = list()
85
+ # See if dihedrals became invalid (collinear atoms)
86
+ if check_dihedrals:
87
+ are_valid = [dihedral_valid(new_coords3d, prim.inds) for prim in dihedrals]
88
+ try:
89
+ first_dihedral = dihedral_inds[0]
90
+ except IndexError:
91
+ first_dihedral = 0
92
+ invalid_inds = [
93
+ i + first_dihedral for i, is_valid in enumerate(are_valid) if not is_valid
94
+ ]
95
+
96
+ if check_bends and len(bend_inds) > 0:
97
+ bends = [prim_internals[i] for i in bend_inds]
98
+ are_valid = [
99
+ bend_valid(new_coords3d, prim.inds, bend_min_deg, bend_max_deg)
100
+ for prim in bends
101
+ ]
102
+ first_bend = bend_inds[0]
103
+ invalid_inds = [
104
+ i + first_bend for i, is_valid in enumerate(are_valid) if not is_valid
105
+ ]
106
+
107
+ if len(invalid_inds) > 0:
108
+ invalid_prims = [primitives[i] for i in invalid_inds]
109
+ invalid_msg = ", ".join([str(tp) for tp in invalid_prims])
110
+ log(logger, "Internal coordinate(s) became invalid! Need new internal coordinates!")
111
+ log(logger, f"Invalid primitives: {invalid_msg}")
112
+ raise NeedNewInternalsException(
113
+ new_coords3d, invalid_inds=invalid_inds, invalid_prims=invalid_prims
114
+ )
115
+
116
+ return prim_internals
117
+
118
+
119
+ def inds_from_prim_types(typed_prims, prim_types):
120
+ inds = [
121
+ i for i, (prim_type, *inds) in enumerate(typed_prims) if prim_type in prim_types
122
+ ]
123
+ return inds
124
+
125
+
126
+ def transform_int_step(
127
+ int_step,
128
+ old_cart_coords,
129
+ cur_internals,
130
+ Bt_inv_prim,
131
+ primitives,
132
+ typed_prims=None,
133
+ dihedral_inds=None,
134
+ rotation_inds=None,
135
+ bend_inds=None,
136
+ check_dihedrals=False,
137
+ check_bends=False,
138
+ bend_min_deg=BEND_MIN_DEG,
139
+ bend_max_deg=LB_MIN_DEG,
140
+ freeze_atoms=None,
141
+ constrained_inds=None,
142
+ update_constraints=False,
143
+ cart_rms_thresh=1e-6,
144
+ Bt_inv_prim_getter=None,
145
+ max_cycles=25,
146
+ logger=None,
147
+ ):
148
+ """Transformation is done in primitive internals, so int_step must be given
149
+ in primitive internals and not in DLC!"""
150
+
151
+ # If an iterable of typed prims is given we can derive bend/dihedral/rotation
152
+ # indices from them.
153
+ if typed_prims is not None:
154
+ if dihedral_inds is None:
155
+ dihedral_inds = inds_from_prim_types(typed_prims, Dihedrals)
156
+
157
+ if bend_inds is None:
158
+ bend_inds = inds_from_prim_types(typed_prims, Bends)
159
+
160
+ if rotation_inds is None:
161
+ rotation_inds = inds_from_prim_types(typed_prims, Rotations)
162
+
163
+ if freeze_atoms is None:
164
+ freeze_atoms = list()
165
+
166
+ if constrained_inds is None:
167
+ constrained_inds = list()
168
+
169
+ freeze_atoms = np.array(freeze_atoms, dtype=int)
170
+ new_cart_coords = old_cart_coords.copy()
171
+ remaining_int_step = int_step
172
+ target_internals = cur_internals + int_step
173
+ # When we want to update the constraints we use the target primitive internals,
174
+ if update_constraints:
175
+ constrained_vals = target_internals[constrained_inds]
176
+ # otherwise we try to stay with the original constrained values.
177
+ else:
178
+ constrained_vals = cur_internals[constrained_inds]
179
+
180
+ def backtransform(remaining_int_step, Bt_inv_prim):
181
+ """Separate function so it can be a called after the main loop
182
+ finished to re-enforce the constraints."""
183
+ nonlocal new_cart_coords
184
+
185
+ cart_step = Bt_inv_prim.T.dot(remaining_int_step)
186
+ # Remove step from frozen atoms.
187
+ cart_step.reshape(-1, 3)[freeze_atoms] = 0.0
188
+ cart_rms = np.sqrt(np.mean(cart_step**2))
189
+ # Update cartesian coordinates
190
+ new_cart_coords += cart_step
191
+ # Determine new internal coordinates
192
+ new_prim_ints = update_internals(
193
+ new_cart_coords.reshape(-1, 3),
194
+ old_internals,
195
+ primitives,
196
+ dihedral_inds,
197
+ rotation_inds,
198
+ bend_inds,
199
+ check_dihedrals=check_dihedrals,
200
+ check_bends=check_bends,
201
+ bend_min_deg=bend_min_deg,
202
+ bend_max_deg=bend_max_deg,
203
+ logger=logger,
204
+ )
205
+ new_internals = [prim.val for prim in new_prim_ints]
206
+ return new_prim_ints, new_internals, cart_rms
207
+
208
+ last_rms = 9999
209
+ old_internals = cur_internals
210
+ backtransform_failed = True
211
+ for i in range(max_cycles):
212
+ if Bt_inv_prim_getter is not None:
213
+ Bt_inv_prim = Bt_inv_prim_getter(new_cart_coords)
214
+ log(logger, f"Recalculated (B^T)^+ in microcycle {i}")
215
+
216
+ new_prim_ints, new_internals, cart_rms = backtransform(
217
+ remaining_int_step, Bt_inv_prim
218
+ )
219
+ remaining_int_step = target_internals - new_internals
220
+ internal_rms = np.sqrt(np.mean(remaining_int_step**2))
221
+ log(
222
+ logger,
223
+ f"Cycle {i}: rms(Δcart)={cart_rms:1.4e}, rms(Δint.) = {internal_rms:1.5e}",
224
+ )
225
+
226
+ # This assumes the first cart_rms won't be > 9999 ;)
227
+ if cart_rms < last_rms:
228
+ # Store results of the conversion cycle for laster use, if
229
+ # the internal-cartesian-transformation goes bad.
230
+ best_cycle = (new_cart_coords.copy(), new_internals.copy())
231
+ best_cycle_ind = i
232
+ elif i != 0:
233
+ # If the conversion somehow fails we fallback to the best previous step.
234
+ log(logger, f"Backconversion failed! Falling back to step {best_cycle_ind}")
235
+ new_cart_coords, new_internals = best_cycle
236
+ break
237
+ else:
238
+ raise Exception(
239
+ "Internal-cartesian back-transformation already "
240
+ "failed in the first step. Aborting!"
241
+ )
242
+ old_internals = new_internals
243
+
244
+ last_rms = cart_rms
245
+ if cart_rms < cart_rms_thresh:
246
+ log(
247
+ logger,
248
+ f"Internal->Cartesian transformation converged in {i+1} cycle(s)!",
249
+ )
250
+ backtransform_failed = False
251
+ break
252
+
253
+ if len(constrained_inds) > 0:
254
+ for j in range(max_cycles):
255
+ cur_constrained_vals = np.array(new_internals)[constrained_inds]
256
+ diff = constrained_vals - cur_constrained_vals
257
+ if any(np.abs(diff) <= 1e-5):
258
+ break
259
+ remaining_int_step = np.zeros_like(remaining_int_step)
260
+ remaining_int_step[constrained_inds] = diff
261
+ new_prim_ints, new_internals, _ = backtransform(
262
+ remaining_int_step, Bt_inv_prim
263
+ )
264
+ if j > 0:
265
+ log(logger, f"Re-enforced constraints in {j} additional cycle(s).")
266
+
267
+ log(logger, "")
268
+
269
+ # Return the difference between the new cartesian coordinates that yield
270
+ # the desired internal coordinates and the old cartesian coordinates.
271
+ cart_step = new_cart_coords - old_cart_coords
272
+ return new_prim_ints, cart_step, backtransform_failed
@@ -0,0 +1,89 @@
1
+ import numpy as np
2
+
3
+ from pysisyphus.helpers_pure import log
4
+ from pysisyphus.intcoords.PrimTypes import PrimTypes
5
+ from pysisyphus.intcoords import Bend
6
+ from pysisyphus.linalg import norm3
7
+
8
+
9
+ def bend_valid(coords3d, indices, min_deg, max_deg):
10
+ val = Bend._calculate(coords3d, indices)
11
+ deg = np.rad2deg(val)
12
+ return min_deg <= deg <= max_deg
13
+
14
+
15
+ def are_collinear(vec1, vec2, deg_thresh=179.5):
16
+ thresh = np.cos(np.deg2rad(deg_thresh))
17
+ return abs(vec1.dot(vec2)) >= abs(thresh)
18
+
19
+
20
+ def dihedral_valid(coords3d, inds, deg_thresh=177.5):
21
+ if len(set(inds)) != 4:
22
+ return False
23
+
24
+ m, o, p, n = inds
25
+ u_dash = coords3d[m] - coords3d[o]
26
+ v_dash = coords3d[n] - coords3d[p]
27
+ w_dash = coords3d[p] - coords3d[o]
28
+ u_norm = norm3(u_dash)
29
+ v_norm = norm3(v_dash)
30
+ w_norm = norm3(w_dash)
31
+ u = u_dash / u_norm
32
+ v = v_dash / v_norm
33
+ w = w_dash / w_norm
34
+
35
+ valid = not (
36
+ are_collinear(u, w, deg_thresh=deg_thresh)
37
+ or are_collinear(v, w, deg_thresh=deg_thresh)
38
+ )
39
+ return valid
40
+
41
+
42
+ def dihedrals_are_valid(coords3d, dihedral_inds, logger=None):
43
+ valid = [dihedral_valid(coords3d, inds) for inds in dihedral_inds]
44
+ invalid = [dihedral_ind for dihedral_ind, v in zip(dihedral_inds, valid) if not v]
45
+ if invalid:
46
+ log(logger, f"Invalid dihedrals: {invalid}")
47
+ all_valid = all(valid)
48
+ return all_valid
49
+
50
+
51
+ def check_typed_prims(
52
+ coords3d,
53
+ typed_prims,
54
+ bend_min_deg,
55
+ dihed_max_deg,
56
+ lb_min_deg,
57
+ logger=None,
58
+ check_bends=True,
59
+ ):
60
+ if check_bends:
61
+ bend_func = lambda indices: bend_valid(
62
+ coords3d, indices, min_deg=bend_min_deg, max_deg=lb_min_deg
63
+ )
64
+ else:
65
+ bend_func = lambda indices: True
66
+ funcs = {
67
+ PrimTypes.BEND: bend_func,
68
+ PrimTypes.PROPER_DIHEDRAL: lambda indices: dihedral_valid(
69
+ coords3d,
70
+ indices,
71
+ deg_thresh=dihed_max_deg,
72
+ ),
73
+ PrimTypes.IMPROPER_DIHEDRAL: lambda indices: dihedral_valid(
74
+ coords3d,
75
+ indices,
76
+ deg_thresh=dihed_max_deg,
77
+ ),
78
+ }
79
+ valid_typed_prims = list()
80
+ for i, (type_, *indices) in enumerate(typed_prims):
81
+ try:
82
+ valid = funcs[type_](indices)
83
+ except KeyError:
84
+ valid = True
85
+ if valid:
86
+ valid_typed_prims.append((type_, *indices))
87
+ else:
88
+ log(logger, f"Primitive {(type_, *indices)} is invalid!")
89
+ return valid_typed_prims
@@ -0,0 +1,93 @@
1
+ # [1] https://doi.org/10.1063/1.5090303
2
+ # Geodesic interpolation for reaction pathways
3
+ # Zhu, Thompson, Martinez, 2019
4
+
5
+ import numpy as np
6
+
7
+ from pysisyphus.Geometry import Geometry
8
+ from pysisyphus.interpolate.Interpolator import Interpolator
9
+ from pysisyphus.constants import BOHR2ANG
10
+
11
+ try:
12
+ from geodesic_interpolate.interpolation import redistribute
13
+ from geodesic_interpolate.geodesic import Geodesic as Geo
14
+
15
+ can_geodesic = True
16
+ except ImportError:
17
+ can_geodesic = False
18
+
19
+
20
+ class Geodesic(Interpolator):
21
+ def __init__(
22
+ self,
23
+ *args,
24
+ align: bool = False,
25
+ tol: float = 2e-3,
26
+ scaling: float = 1.7,
27
+ dist_cutoff: float = 3.0,
28
+ friction: float = 1e-2,
29
+ maxiter: int = 15,
30
+ microiter: int = 20,
31
+ **kwargs,
32
+ ):
33
+ """Geodesic Interpolation for Reaction Pathways.
34
+
35
+ Requires the 'geodesic-interpolate' package found at
36
+
37
+ https://github.com/virtualzx-nad/geodesic-interpolate.git
38
+
39
+ Parameters
40
+ ----------
41
+ align : bool, optional
42
+ Whether to align geometries, by default False
43
+ tol : float, optional
44
+ Convergence tolerance, by default 2e-3
45
+ scaling : float, optional
46
+ Exponential parameter for morse potential, by default 1.7
47
+ dist_cutoff : float, optional
48
+ Cut-off value for the distance between a pair of atoms
49
+ to be included in the coordinate system, by default 3.0
50
+ friction : float, optional
51
+ Size of friction term used to prevent very large
52
+ change of geometry, by default 1e-2
53
+ maxiter : int, optional
54
+ Maximum number of minimization iterations, by default 15
55
+ microiter : int, optional
56
+ Maximum number of micro iterations for
57
+ sweeping algorithm, by default 20
58
+ """
59
+ super().__init__(*args, align=align, **kwargs)
60
+ self.tol = tol
61
+ self.scaling = scaling
62
+ self.dist_cutoff = dist_cutoff
63
+ self.friction = friction
64
+ self.maxiter = maxiter
65
+ self.microiter = microiter
66
+
67
+ def interpolate(self, initial_geom, final_geom, **kwargs):
68
+ if not can_geodesic:
69
+ raise ModuleNotFoundError(
70
+ "Geodesic interpolation requires the geodesic_interpolate package."
71
+ )
72
+ coords3d = np.array((initial_geom.coords3d, final_geom.coords3d))
73
+ coords3d *= BOHR2ANG
74
+
75
+ nimages = 2 + self.between
76
+ raw = redistribute(self.atoms, coords3d, nimages, tol=self.tol * 5)
77
+
78
+ smoother = Geo(
79
+ self.atoms,
80
+ raw,
81
+ self.scaling,
82
+ threshold=self.dist_cutoff,
83
+ friction=self.friction,
84
+ )
85
+ if len(self.atoms) > 35:
86
+ path = smoother.sweep(
87
+ tol=self.tol, max_iter=self.maxiter, micro_iter=self.microiter
88
+ )
89
+ else:
90
+ path = smoother.smooth(tol=self.tol, max_iter=self.maxiter)
91
+ path /= BOHR2ANG
92
+ interpolated_geoms = [Geometry(self.atoms, coords) for coords in path]
93
+ return interpolated_geoms[1:-1]
@@ -0,0 +1,55 @@
1
+ from scipy.spatial.distance import pdist
2
+
3
+ # from pysisyphus.constants import BOHR2ANG, ANG2BOHR
4
+ from pysisyphus.calculators.IDPPCalculator import IDPPCalculator
5
+ from pysisyphus.constants import BOHR2ANG, ANG2BOHR
6
+ from pysisyphus.cos.NEB import NEB
7
+ from pysisyphus.helpers import align_geoms
8
+ from pysisyphus.optimizers.FIRE import FIRE
9
+ from pysisyphus.interpolate.Interpolator import Interpolator
10
+
11
+
12
+ # [1] http://aip.scitation.org/doi/full/10.1063/1.4878664
13
+ # See https://gitlab.com/ase/ase/blob/master/ase/neb.py
14
+
15
+
16
+ class IDPP(Interpolator):
17
+
18
+ def interpolate(self, initial_geom, final_geom, **kwargs):
19
+ # Do an initial linear interpolation to generate all geometries/images
20
+ # that will be refined later by IDPP interpolation.
21
+ linear_interpol = super().interpolate(initial_geom, final_geom)
22
+ idpp_geoms = [initial_geom] + linear_interpol + [final_geom]
23
+ align_geoms(idpp_geoms)
24
+
25
+ # Interestingly IDPP calculations work much better when done
26
+ # in Angstroem instead of in Bohr.
27
+ for geom in idpp_geoms:
28
+ geom.coords *= BOHR2ANG
29
+
30
+ # We want to interpolate between these two condensed distance matrices
31
+ initial_pd = pdist(initial_geom.coords3d)
32
+ final_pd = pdist(final_geom.coords3d)
33
+ steps = 1 + self.between
34
+ pd_diff = (final_pd - initial_pd) / steps
35
+
36
+ for i, geom in enumerate(idpp_geoms):
37
+ geom.set_calculator(IDPPCalculator(initial_pd + i * pd_diff))
38
+
39
+ neb = NEB(idpp_geoms)
40
+ opt_kwargs = {
41
+ "max_cycles": 1000,
42
+ "rms_force": 1e-2,
43
+ "align": False,
44
+ "check_coord_diffs": False,
45
+ }
46
+ opt = FIRE(neb, **opt_kwargs)
47
+ opt.run()
48
+
49
+ for geom in idpp_geoms:
50
+ # Delete IDPP calculator, energies and forces
51
+ geom.clear()
52
+ geom.coords *= ANG2BOHR
53
+
54
+ interpolated_geoms = idpp_geoms[1:-1]
55
+ return interpolated_geoms