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,63 @@
1
+ import random
2
+
3
+ import numpy as np
4
+
5
+ class TableFormatter:
6
+
7
+ def __init__(self, header, fmts, min_width=7, space=3):
8
+ self.min_width = min_width + (space-1)
9
+ self.space = space
10
+
11
+ # Get lengths of header strings
12
+ widths = np.array([len(h) for h in header])
13
+ # Expand entries smaller than min_widht to min_width
14
+ smaller_indices = widths < min_width
15
+ widths[smaller_indices] = min_width
16
+ self.widths = widths
17
+
18
+ # Construct header
19
+ #header_fmts = ["{:" + "{}".format(width) + "s}"
20
+ # for width in self.widths]
21
+ header_fmts = self.min_width_fmts()
22
+ self._header = self.join_format(header_fmts, header)
23
+ self._header += "\n" + (self.space*" ").join(
24
+ ["-"*width for width in self.widths]
25
+ )
26
+
27
+ # Modify fmts to consinder min_widths
28
+ self.fmts = self.min_width_fmts(fmts)
29
+
30
+ def min_width_fmts(self, raw_fmts=None):
31
+ if not raw_fmts:
32
+ raw_fmts = ["s" for _ in self.widths]
33
+ return ["{:>" + "{}".format(width) + fmt + "}"
34
+ for width, fmt in zip(self.widths, raw_fmts)]
35
+
36
+ def join_format(self, fmts, lst):
37
+ """Format a given iterable lst with formats given in the iterable
38
+ lst and return the joined items of the formatted list."""
39
+ return (self.space*" ").join(
40
+ [fmt.format(item) for fmt, item in zip(fmts, lst)]
41
+ )
42
+
43
+ @property
44
+ def header(self):
45
+ return self._header
46
+
47
+ def line(self, *args):
48
+ #formatted = " "["
49
+ return self.join_format(self.fmts, args)
50
+
51
+ def run():
52
+ header = "# |dx| |tangent|".split()
53
+ fmts = ["d", ".2E", ".3E"]
54
+ min_width = 10
55
+ tp = TableFormatter(header, fmts, min_width)
56
+ print(tp.header)
57
+ for i in range(10):
58
+ dx = random.random()
59
+ tangent = random.random()
60
+ print(tp.line(i, dx, tangent))
61
+
62
+ if __name__ == "__main__":
63
+ run()
@@ -0,0 +1,74 @@
1
+ import textwrap
2
+
3
+
4
+ class TablePrinter:
5
+ def __init__(
6
+ self,
7
+ header,
8
+ col_fmts,
9
+ width=12,
10
+ sub_underline=True,
11
+ shift_left=0,
12
+ fmts_update=None,
13
+ mark="*",
14
+ ):
15
+ self.header = header
16
+ self.col_fmts = col_fmts
17
+ self.width = width
18
+ self.sub_underline = sub_underline
19
+ self.shift_left = shift_left
20
+ if fmts_update is None:
21
+ fmts_update = {}
22
+ self.fmts_update = fmts_update
23
+ self.mark = mark
24
+
25
+ w = str(self.width - 1)
26
+
27
+ self.fmts = {
28
+ "int": "{:>" + w + "d}",
29
+ "float": "{: >" + w + ".6f}",
30
+ "float_short": "{: >" + w + ".2f}",
31
+ "str": "{:>" + w + "s}",
32
+ "mark": "{:>1s}",
33
+ }
34
+ self.fmts.update(fmts_update)
35
+ if self.sub_underline:
36
+ self.header = [h.replace("_", " ") for h in self.header]
37
+
38
+ self.header_str = " ".join([h.rjust(self.width) for h in self.header])
39
+ mark_fmt = self.fmts["mark"]
40
+ self.conv_str = " ".join([self.fmts[fmt] + mark_fmt for fmt in self.col_fmts])
41
+ h0_len = len(self.header[0])
42
+ self.offset = self.width - h0_len
43
+ self.prefix = " " * (self.offset - self.shift_left)
44
+ self.sep = (
45
+ self.prefix
46
+ + "-" * (len(self.header_str) - self.width + h0_len)
47
+ + "-" * abs(self.shift_left)
48
+ )
49
+
50
+ def print_sep(self):
51
+ print(self.sep)
52
+
53
+ def print_header(self, with_sep=True):
54
+ print(self.header_str)
55
+ if with_sep:
56
+ self.print_sep()
57
+
58
+ def print_row(self, args, marks=None):
59
+ if marks is None:
60
+ marks = ["" for _ in args]
61
+ marked_args = list()
62
+ for arg, to_mark in zip(args, marks):
63
+ marked_args.append(arg)
64
+ marked_args.append(self.mark if to_mark else " ")
65
+ print(self.conv_str.format(*marked_args))
66
+
67
+ def print(self, *args, **kwargs):
68
+ text = " ".join([str(a) for a in args])
69
+ try:
70
+ level = kwargs["level"]
71
+ except KeyError:
72
+ level = 0
73
+ level_prefix = " " * level
74
+ print(textwrap.indent(text, self.prefix + level_prefix))
pysisyphus/__init__.py ADDED
@@ -0,0 +1,12 @@
1
+ import logging
2
+ import sys
3
+
4
+ logger = logging.getLogger("pysisyphus")
5
+ logger.setLevel(logging.DEBUG)
6
+
7
+ file_handler = logging.FileHandler("pysisyphus.log", mode="w", delay=True)
8
+ logger.addHandler(file_handler)
9
+
10
+ stdout_handler = logging.StreamHandler(sys.stdout)
11
+ stdout_handler.setLevel(logging.INFO)
12
+ logger.addHandler(stdout_handler)
@@ -0,0 +1,452 @@
1
+ # [1] https://pubs.acs.org/doi/pdf/10.1021/ct200290m?rand=dcfwsf09
2
+ # [2] https://onlinelibrary.wiley.com/doi/epdf/10.1002/jcc.23481
3
+ # [3] https://onlinelibrary.wiley.com/doi/epdf/10.1002/tcr.201600043
4
+
5
+ import itertools as it
6
+ from math import isclose
7
+ from typing import List
8
+
9
+ import autograd
10
+ import autograd.numpy as anp
11
+ import numpy as np
12
+ import numpy.typing as npt
13
+
14
+ from pysisyphus.calculators.Calculator import Calculator
15
+ from pysisyphus.elem_data import COVALENT_RADII
16
+ from pysisyphus.Geometry import Geometry
17
+ from pysisyphus.helpers import complete_fragments
18
+ from pysisyphus.helpers_pure import log
19
+ from pysisyphus.io.hdf5 import get_h5_group, resize_h5_group
20
+ from pysisyphus.linalg import finite_difference_hessian
21
+
22
+
23
+ def get_data_model(atoms, max_cycles):
24
+ coord_size = 3 * len(atoms)
25
+ _1d = (max_cycles,)
26
+ _2d = (max_cycles, coord_size)
27
+ _3d = (max_cycles, coord_size, coord_size)
28
+
29
+ data_model = {
30
+ "cart_coords": _2d,
31
+ "energy": _1d,
32
+ "forces": _2d,
33
+ "hessian": _3d,
34
+ "true_energy": _1d,
35
+ "true_forces": _2d,
36
+ "true_hessian": _3d,
37
+ }
38
+
39
+ return data_model
40
+
41
+
42
+ class CovRadiiSumZero(Exception):
43
+ pass
44
+
45
+
46
+ def afir_closure(
47
+ fragment_indices, cov_radii, gamma, rho=1, p=6, prefactor=1.0, logger=None
48
+ ):
49
+ """rho=1 pushes fragments together, rho=-1 pulls fragments apart."""
50
+
51
+ # See https://onlinelibrary.wiley.com/doi/full/10.1002/qua.24757
52
+ # Eq. (9) for extension to 3 fragments.
53
+ assert len(fragment_indices) == 2
54
+
55
+ inds = np.array(list(it.product(*fragment_indices)))
56
+ cov_rad_sums = cov_radii[inds].sum(axis=1)
57
+
58
+ if isclose(cov_rad_sums.sum(), 0.0, abs_tol=1e-10):
59
+ raise CovRadiiSumZero("Sum of covalent radii is 0.0!")
60
+
61
+ # 3.8164 Angstrom in Bohr
62
+ R0 = 7.21195
63
+ # 1.0061 kJ/mol to Hartree
64
+ epsilon = 0.000383203368
65
+
66
+ # Avoid division by zero for gamma = 0.
67
+ if gamma == 0.0:
68
+ alpha = 0.0
69
+ else:
70
+ alpha = gamma / (
71
+ (2 ** (-1 / 6) - (1 + (1 + gamma / epsilon) ** 0.5) ** (-1 / 6)) * R0
72
+ )
73
+
74
+ rho_verbose = {1: ("pushing", "together"), -1: ("pulling", "apart")}
75
+ w1, w2 = rho_verbose[rho]
76
+ log(
77
+ logger,
78
+ f"Creating AFIR closure with α={alpha:.6f}, prefactor {prefactor:.6f}, "
79
+ f"rho={rho}, {w1} framgents {w2}",
80
+ )
81
+
82
+ def afir_func(coords3d):
83
+ diffs = anp.diff(coords3d[inds], axis=1).reshape(-1, 3)
84
+ rs = anp.linalg.norm(diffs, axis=1)
85
+
86
+ omegas = (cov_rad_sums / rs) ** p
87
+
88
+ f = prefactor * alpha * rho * (omegas * rs).sum() / omegas.sum()
89
+ return f
90
+
91
+ return afir_func
92
+
93
+
94
+ class AFIR(Calculator):
95
+ def __init__(
96
+ self,
97
+ calculator: Calculator,
98
+ fragment_indices: List[List[int]],
99
+ gamma: npt.ArrayLike,
100
+ rho: npt.ArrayLike = 1,
101
+ p: int = 6,
102
+ ignore_hydrogen: bool = False,
103
+ zero_hydrogen: bool = True,
104
+ complete_fragments: bool = True,
105
+ dump: bool = True,
106
+ h5_fn: str = "afir.h5",
107
+ h5_group_name: str = "afir",
108
+ **kwargs,
109
+ ):
110
+ """
111
+ Artifical Force Induced Reaction calculator.
112
+
113
+ Currently, there are no automated drivers to run large-scale AFIR calculations
114
+ with many different initial orientations and/or increasing collision energy
115
+ parameter γ. Nontheless, selected AFIR calculations can be carried out by hand.
116
+ After convergence, artificial potential & forces, as well as real energies
117
+ and forces can be plotted with 'pysisplot --afir'. The highest energy point
118
+ along the AFIR path can then be selected for a subsequent TS-optimization,
119
+ e.g. via 'pysistrj --get [index] optimzation_trj.xyz'.
120
+
121
+ Future versions of pysisyphus may provide drivers for more automatted
122
+ AFIR calculations.
123
+
124
+ Parameters
125
+ ----------
126
+ calculator
127
+ Actual QC calculator that provides energies and its derivatives,
128
+ that are modified by the AFIR calculator, e.g., ORCA or Psi4.
129
+ fragment_indices
130
+ List of lists of integers, specifying the separate fragments. If
131
+ the indices in theses lists don't comprise all atoms in the molecule,
132
+ the reamining indices will be added as a separate fragment. If a AFIR
133
+ calculation is carried out with 2 fragments and 'complete_fragments' is
134
+ True (see below) it is enough to specify only
135
+ the indices of one fragment, e.g., for a system of 10 atoms
136
+ 'fragment_indices=[[0,1,2,3]]' is enough. The second system will be set
137
+ up automatically with indices [4,5,6,7,8,9].
138
+ gamma
139
+ Collision energy parameter γ in au. For 2 fragments it can be a single
140
+ integer, while for > 2 fragments a list of gammas must be given, specifying
141
+ the pair-wise collision energy parameters. For 3 fragments 3 gammas must be
142
+ given [γ_01, γ_02, γ_12], for 4 fragments 6 gammas would be required
143
+ [γ_01, γ_02, γ_03, γ_12, γ_13, γ_23] and so on.
144
+ rho
145
+ Direction of the artificial force, either 1 or -1. The same comments
146
+ as for gamma apply. For 2 fragments a single integer is enough, for
147
+ > 2 fragments a list of rhos must be given (see above). For rho=1
148
+ fragments are pushed together, for rho=-1 fragments are pulled apart.
149
+ p
150
+ Exponent p used in the calculation of the weight function ω. Defaults
151
+ to 6 and probably does not have to be changed.
152
+ ignore_hydrogen
153
+ Whether hydrogens are ignored in the calculation of the artificial force.
154
+ All weights between atom pairs containing hydrogen will be set to 0.
155
+ zero_hydrogen
156
+ Whether to use 0.0 as covalent radius for hydrogen in the weight function.
157
+ Compared to 'ignore_hydrogen', which results in zero weights for all atom
158
+ pairs involving hydrogen, 'zero_hydrogen' may be non-zero, depending on the
159
+ covalent radius of the second atom in the pair.
160
+ complete_fragments
161
+ Whether an incomplete specification in 'fragment_indices' is automatically
162
+ completed.
163
+ dump
164
+ Whether an HDF5 file is created.
165
+ h5_fn
166
+ Filename of the HDF5 file used for dumping.
167
+ h5_group_name
168
+ HDF5 group name used for dumping.
169
+
170
+ Other Parameters
171
+ ----------------
172
+ **kwargs
173
+ Keyword arguments passed to the Calculator baseclass.
174
+ """
175
+ super().__init__(**kwargs)
176
+
177
+ self.calculator = calculator
178
+ # Update own charge and multiplicity with values from the wrapped
179
+ # calculator.
180
+ self.charge = calculator.charge
181
+ self.mult = calculator.mult
182
+ self.fragment_indices = fragment_indices
183
+ assert len(self.fragment_indices) > 0
184
+ self.gamma = np.atleast_1d(gamma).astype(float)
185
+ self.rho = np.atleast_1d(rho).astype(int)
186
+ np.testing.assert_allclose(np.abs(self.rho), np.ones_like(self.rho))
187
+ self.p = p
188
+ self.ignore_hydrogen = ignore_hydrogen
189
+ self.zero_hydrogen = zero_hydrogen
190
+ self.complete_fragments = complete_fragments
191
+ self.dump = dump
192
+ self.h5_fn = self.out_dir / h5_fn
193
+ self.h5_group_name = h5_group_name
194
+ # We can't initialize the HDF5 group as we don't know the shape of
195
+ # atoms/coords yet. So we wait until after the first calculation.
196
+ self.h5_cycles = 50
197
+
198
+ if self.ignore_hydrogen:
199
+ self.log("No artificial force contribution from hydrogens!")
200
+ self.atoms = None
201
+ self.calc_counter = 0
202
+ self.h5_group_initialized = False
203
+
204
+ """We try to keep charge and multiplicity consistent between the AFIR
205
+ calculator and the actual wrapped calculator. But we will always return
206
+ the charge and multiplicity of the underlying calculator."""
207
+
208
+ @property
209
+ def charge(self):
210
+ return self.calculator.charge
211
+
212
+ @charge.setter
213
+ def charge(self, charge):
214
+ try:
215
+ self.calculator.charge = charge
216
+ except AttributeError:
217
+ pass
218
+ self._charge = charge
219
+
220
+ @property
221
+ def mult(self):
222
+ return self.calculator.mult
223
+
224
+ @mult.setter
225
+ def mult(self, mult):
226
+ try:
227
+ self.calculator.mult = mult
228
+ except AttributeError:
229
+ pass
230
+ self._mult = mult
231
+
232
+ def init_h5_group(self, atoms, max_cycles=None):
233
+ if max_cycles is None:
234
+ max_cycles = self.h5_cycles
235
+ self.data_model = get_data_model(atoms, max_cycles)
236
+ # h5_group = get_h5_group(
237
+ # self.h5_fn, self.h5_group_name, self.data_model, reset=True
238
+ # )
239
+ self.h5_group_initialized = True
240
+ # return h5_group
241
+
242
+ def dump_h5(self, atoms, coords, results):
243
+ # Initialize if not done yet
244
+ # if not self.h5_group_initialized:
245
+ # h5_group = self.init_h5_group(atoms)
246
+ # # Write atoms only once
247
+ # h5_group.attrs["atoms"] = atoms
248
+
249
+ # h5_group = get_h5_group(self.h5_fn, self.h5_group_name)
250
+
251
+ # # Check if HDF5 datasets have to be resized
252
+ # cur_max_cycles = h5_group["cart_coords"].shape[0]
253
+ # need_resize = self.calc_counter > cur_max_cycles - 5
254
+ # if need_resize:
255
+ # new_max_cycles = cur_max_cycles + self.h5_cycles
256
+ # resize_h5_group(h5_group, max_cycles=new_max_cycles)
257
+
258
+ # for k, v in results.items():
259
+ # h5_group[k][self.calc_counter] = v
260
+ # h5_group["cart_coords"][self.calc_counter] = coords
261
+ # h5_group.attrs["cur_cycle"] = self.calc_counter
262
+ # h5_group.file.close()
263
+ pass
264
+
265
+ def log_fragments(self):
266
+ self.log(f"Using {len(self.fragment_indices)} fragments")
267
+ for i, frag in enumerate(self.fragment_indices):
268
+ self.log(f"Fragment {i:02d}, {len(frag)} atoms:")
269
+ self.log(f"\t{frag}")
270
+
271
+ def write_fragment_geoms(self, atoms, coords):
272
+ geom = Geometry(atoms, coords)
273
+ for i, frag in enumerate(self.fragment_indices):
274
+ frag_geom = geom.get_subgeom(frag)
275
+ fn = self.make_fn(f"frag_geom_{i:02d}.xyz")
276
+ with open(fn, "w") as handle:
277
+ handle.write(frag_geom.as_xyz())
278
+ self.log(f"Wrote geometry of fragment {i:02d} to {fn}.")
279
+
280
+ def set_atoms_and_funcs(self, atoms, coords):
281
+ """Initially atoms was also an argument to the constructor of AFIR.
282
+ I removed it so creation becomes easier.
283
+ The first time a calculation is requested with a proper atom set
284
+ everything is set up (cov. radii, afir function and corresponding
285
+ gradient). Afterwards there is only a check if atoms != None and it
286
+ is expected that all functions are properly set.
287
+
288
+ fragment_indices can also be incomplete w.r.t. to the number of
289
+ atoms. If the sum of the specified fragment atoms is less than the
290
+ number of atoms present then all remaining unspecified atoms will
291
+ be gathered in one fragment.
292
+ """
293
+
294
+ if self.atoms is not None:
295
+ assert self.atoms == atoms
296
+ return
297
+
298
+ self.log("Setting atoms on AFIR calculator")
299
+ self.atoms = atoms
300
+ if self.complete_fragments:
301
+ self.fragment_indices = complete_fragments(
302
+ self.atoms, self.fragment_indices
303
+ )
304
+ self.log_fragments()
305
+ self.write_fragment_geoms(atoms, coords)
306
+
307
+ hydrogen_inds = [i for i, atom in enumerate(atoms) if atom.lower() == "h"]
308
+ if self.ignore_hydrogen:
309
+ fragment_indices = list()
310
+ for i, frag_inds in enumerate(self.fragment_indices):
311
+ frag_inds_no_h = [j for j in frag_inds if j not in hydrogen_inds]
312
+ fragment_indices.append(frag_inds_no_h)
313
+ dropped_hydrogens = len(frag_inds) - len(frag_inds_no_h)
314
+ self.log(f"Ignoring {dropped_hydrogens} hydrogen(s) from fragment {i}.")
315
+ self.fragment_indices = fragment_indices
316
+
317
+ self.cov_radii_org = np.array([COVALENT_RADII[atom.lower()] for atom in atoms])
318
+ self.cov_radii = self.cov_radii_org.copy()
319
+ if self.zero_hydrogen:
320
+ self.cov_radii[hydrogen_inds] = 0.0
321
+ self.log("Set covalent radii")
322
+ self.afir_funcs = list()
323
+ self.afir_grad_funcs = list()
324
+ pairs = list(it.combinations(self.fragment_indices, 2))
325
+ prefactor = 1 / len(pairs)
326
+ self.log(
327
+ f"Doing AFIR with {len(pairs)} fragment pairs and prefactor={prefactor:.4f}"
328
+ )
329
+ try:
330
+ self.gamma = [float(self.gamma)] * len(pairs)
331
+ except TypeError:
332
+ assert len(self.gamma) == len(pairs)
333
+ try:
334
+ self.rho = [float(self.rho)] * len(pairs)
335
+ except TypeError:
336
+ assert len(self.rho) == len(pairs)
337
+ self.log(f"Using gamma(s): {self.gamma}")
338
+ self.log(f" Using rho(s): {self.rho}")
339
+
340
+ for (frag1, frag2), gamma, rho in zip(pairs, self.gamma, self.rho):
341
+ afir_func = afir_closure(
342
+ (frag1, frag2),
343
+ self.cov_radii,
344
+ gamma=gamma,
345
+ rho=rho,
346
+ p=self.p,
347
+ prefactor=prefactor,
348
+ logger=self.logger,
349
+ )
350
+ afir_grad_func = autograd.grad(afir_func)
351
+ self.afir_funcs.append(afir_func)
352
+ self.afir_grad_funcs.append(afir_grad_func)
353
+ self.log("Created and set AFIR function & gradient function.")
354
+
355
+ def get_energy(self, atoms, coords, **prepare_kwargs):
356
+ self.set_atoms_and_funcs(atoms, coords)
357
+
358
+ true_results = self.calculator.get_energy(atoms, coords, **prepare_kwargs)
359
+ true_energy = true_results["energy"]
360
+ coords3d = coords.reshape(-1, 3)
361
+ # Iterate over all fragment pairs
362
+ afir_energy = sum([afir_func(coords3d) for afir_func in self.afir_funcs])
363
+ self.log()
364
+
365
+ results = {
366
+ "energy": true_energy + afir_energy,
367
+ "true_energy": true_energy,
368
+ }
369
+ if self.dump:
370
+ self.dump_h5(atoms, coords, results)
371
+ self.calc_counter += 1
372
+ return results
373
+
374
+ def get_forces(self, atoms, coords, **prepare_kwargs):
375
+ self.set_atoms_and_funcs(atoms, coords)
376
+
377
+ true_results = self.calculator.get_forces(atoms, coords, **prepare_kwargs)
378
+ true_energy = true_results["energy"]
379
+ true_forces = true_results["forces"]
380
+
381
+ coords3d = coords.reshape(-1, 3)
382
+ afir_energy = 0.0
383
+ afir_forces = np.zeros_like(coords)
384
+ # Iterate over all fragment pairs
385
+ for afir_func, afir_grad_func in zip(self.afir_funcs, self.afir_grad_funcs):
386
+ afir_energy += afir_func(coords3d)
387
+ # Add negative of the gradient (the force)
388
+ afir_forces += -afir_grad_func(coords3d).flatten()
389
+
390
+ true_norm = np.linalg.norm(true_forces)
391
+ afir_norm = np.linalg.norm(afir_forces)
392
+ self.log(
393
+ f"\ntrue_energy={true_energy:.6f} au\n"
394
+ f"afir_energy={afir_energy:.6f} au\n"
395
+ f" sum_energy={true_energy+afir_energy:.6f} au\n"
396
+ f"norm(true_forces)={true_norm:.6f} au/bohr\n"
397
+ f"norm(afir_forces)={afir_norm:.6f} au/bohr\n"
398
+ )
399
+
400
+ results = {
401
+ "energy": true_energy + afir_energy,
402
+ "forces": true_forces + afir_forces,
403
+ "true_forces": true_forces,
404
+ "true_energy": true_energy,
405
+ }
406
+ if self.dump:
407
+ self.dump_h5(atoms, coords, results)
408
+ self.calc_counter += 1
409
+ return results
410
+
411
+ def afir_fd_hessian_wrapper(self, coords3d, afir_grad_func):
412
+ coords = coords3d.flatten()
413
+
414
+ def grad_func(coords):
415
+ afir_grad = afir_grad_func(coords.reshape(-1, 3))
416
+ return afir_grad.flatten()
417
+
418
+ fd_hessian = finite_difference_hessian(coords, grad_func, acc=4)
419
+ return fd_hessian
420
+
421
+ def get_hessian(self, atoms, coords, **prepare_kwargs):
422
+ self.set_atoms_and_funcs(atoms, coords)
423
+
424
+ true_results = self.calculator.get_hessian(atoms, coords, **prepare_kwargs)
425
+ true_energy = true_results["energy"]
426
+ true_hessian = true_results["hessian"]
427
+
428
+ coords3d = coords.reshape(-1, 3)
429
+ afir_energy = 0.0
430
+ afir_hessian = np.zeros_like(true_hessian)
431
+ # Iterate over all fragment pairs
432
+ for afir_func, afir_grad_func in zip(self.afir_funcs, self.afir_grad_funcs):
433
+ afir_energy += afir_func(coords3d)
434
+ # AFIR Hessian from finite differences
435
+ afir_hessian += self.afir_fd_hessian_wrapper(coords3d, afir_grad_func)
436
+
437
+ results = {
438
+ "energy": true_energy + afir_energy,
439
+ "hessian": true_hessian + afir_hessian,
440
+ "true_hessian": true_hessian,
441
+ "true_energy": true_energy,
442
+ }
443
+ if self.dump:
444
+ self.dump_h5(atoms, coords, results)
445
+ self.calc_counter += 1
446
+ return results
447
+
448
+ def __repr__(self):
449
+ return self.__str__()
450
+
451
+ def __str__(self):
452
+ return f"AFIR({self.calculator})"
@@ -0,0 +1,20 @@
1
+ import numpy as np
2
+
3
+ from pysisyphus.calculators.AnaPotBase import AnaPotBase
4
+
5
+
6
+ class AnaPot(AnaPotBase):
7
+ def __init__(self, **kwargs):
8
+ kwargs_ = {
9
+ "V_str": "4 + 4.5*x - 4*y + x**2 + 2*y**2-2*x*y + x**4 - 2*x**2*y",
10
+ "xlim": (-2, 2.5),
11
+ "ylim": (0, 5),
12
+ "levels": np.linspace(-3, 4, 80),
13
+ "minima": ((-1.05274, 1.02776, 0), (1.94101, 3.85427, 0)),
14
+ "saddles": ((0.6117313, 1.4929732, 0.0),),
15
+ }
16
+ kwargs_.update(kwargs)
17
+ super().__init__(**kwargs_)
18
+
19
+ def __str__(self):
20
+ return "AnaPot calculator"
@@ -0,0 +1,48 @@
1
+ import numpy as np
2
+ import sympy as sym
3
+ from sympy import atan, exp, tan, sin, pi
4
+
5
+
6
+ from pysisyphus.calculators.AnaPotBase import AnaPotBase
7
+ from pysisyphus.calculators.Calculator import Calculator
8
+
9
+ # https://www.wolframalpha.com/input/?i=plot+arccot(-exp(y)*cot(x/2-pi/4))+-+2*exp(-(y-sin(x))^2/2)
10
+ # [1] http://aip.scitation.org/doi/abs/10.1063/1.461606
11
+ # https://www.wolframalpha.com/input/?i=derivative+of+(arccot(-exp(y)*cot(x/2-pi/4))+-+2*exp(-(y-sin(x))^2/2))
12
+
13
+ class AnaPot2_(Calculator):
14
+
15
+ def __init__(self):
16
+ super().__init__()
17
+
18
+ def get_energy(self, atoms, coords):
19
+ x, y, z = coords
20
+ cot = 1 / np.tan(x/2 - np.pi/4)
21
+ arccot = np.arctan(
22
+ 1 / (-np.exp(y) * cot)
23
+ )
24
+ energy = (
25
+ arccot - 2 * np.exp(-(y-np.sin(x))**2 / 2)
26
+ )
27
+ return {"energy": energy}
28
+
29
+ def __str__(self):
30
+ return "AnaPot2 calculator"
31
+
32
+
33
+ class AnaPot2(AnaPotBase):
34
+ """We can't use sympify as it replaces 1/tan by cot and this isn't
35
+ supported by numpy when we call lambdify."""
36
+
37
+ def __init__(self):
38
+ x, y = sym.symbols("x y")
39
+ V_str = atan(exp(-y)/tan(x/2 + pi/4)) - 2*exp(-(y - sin(x))**2/2)
40
+ # xlim = (-np.pi/2, np.pi)
41
+ xlim = (-np.pi, np.pi)
42
+ ylim = (-2, 2)
43
+ levels = np.linspace(-2, 1, 40)
44
+ super().__init__(V_str=V_str, xlim=xlim, ylim=ylim, levels=levels,
45
+ use_sympify=False)
46
+
47
+ def __str__(self):
48
+ return "AnaPot2 calculator"
@@ -0,0 +1,12 @@
1
+ from pysisyphus.calculators.AnaPotBase import AnaPotBase
2
+
3
+ # [1] http://www.cims.nyu.edu/~eve2/string_jcp_simplified07.pdf
4
+
5
+ class AnaPot3(AnaPotBase):
6
+
7
+ def __init__(self):
8
+ V_str = "(1 - x**2 - y**2)**2 + (y**2) / (x**2 + y**2)"
9
+ super(AnaPot3, self).__init__(V_str=V_str)
10
+
11
+ def __str__(self):
12
+ return "AnaPot3 calculator"