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,127 @@
1
+ import itertools as it
2
+
3
+ import numpy as np
4
+ import rmsd as rmsd
5
+ from scipy.optimize import linear_sum_assignment
6
+ from scipy.spatial.distance import cdist
7
+
8
+
9
+ def match_geom_atoms(ref_geom, geom_to_match, hydrogen=True):
10
+ """Apply the hungarian method to geom_to_match.
11
+
12
+ Uses the scipy implementation of the Hungarian method/
13
+ Kuhn-Munkres algorithm in scipy.optimize.linear_sum_assignment.
14
+
15
+ See
16
+ [1] 10.1021/ci400534h
17
+ [2] 10.1021/acs.jcim.6b00516
18
+ """
19
+
20
+ assert len(ref_geom.atoms) == len(geom_to_match.atoms), \
21
+ "Atom numbers don't match!"
22
+
23
+ ref_coords, _ = ref_geom.coords_by_type
24
+ coords_to_match, inds_to_match = geom_to_match.coords_by_type
25
+ atoms = ref_coords.keys()
26
+
27
+ # The new geometry that will contain the perhaps reordered
28
+ # coordinates.
29
+ geom_matched = geom_to_match.copy()
30
+ for atom in atoms:
31
+ # Don't match hydrogens if not requested
32
+ if atom.lower() == "h" and not hydrogen:
33
+ continue
34
+ ref_coords_for_atom = ref_coords[atom]
35
+ coords_to_match_for_atom = coords_to_match[atom]
36
+ # Pairwise distances between two collections
37
+ # Atoms of ref_geom are along the rows, atoms of geom_to_match
38
+ # along the columns.
39
+ cd = cdist(ref_coords_for_atom, coords_to_match_for_atom)
40
+ # Hungarian method, row_inds are returned already sorted, so
41
+ # we only have to consider col_inds.
42
+ row_inds, col_inds = linear_sum_assignment(cd)
43
+ # Resort the coordinates
44
+ new_coords_for_atom = coords_to_match_for_atom[col_inds]
45
+ # Original indices of the coordinates in the full coords array
46
+ # in the Geometry object.
47
+ old_inds = inds_to_match[atom]
48
+ # Update coordinates and modify the array directly
49
+ c3d = geom_matched.coords3d
50
+ c3d[old_inds] = new_coords_for_atom
51
+ return geom_matched
52
+
53
+
54
+ def apply_transform(coords3d, swap, reflection):
55
+ """Apply axis swaps and reflections to a coordinate array."""
56
+ # Swap axes
57
+ coords3d = coords3d[:, swap]
58
+ # Reflect
59
+ coords3d *= reflection
60
+ return coords3d
61
+
62
+
63
+ def matched_rmsd(geom1, geom2, thresh=5e-2):
64
+ """RMSD for optimally aligned and matched geometries.
65
+
66
+ Returns
67
+ -------
68
+ matched_rmsd : float
69
+ RMSD of optimally aligned and matched geometries.
70
+ matched_geoms : tuple(Geometry, Geometry)
71
+ Tuple of the optimally aligned and matched geometries.
72
+ """
73
+
74
+ # Work on copies of the Geometries, as calling standard_orientation
75
+ # moves their coordinates.
76
+ geom1_copy = geom1.copy()
77
+ geom1_copy.standard_orientation()
78
+ coords3d_1 = geom1_copy.coords3d
79
+ geom2_copy = geom2.copy()
80
+ geom2_copy.standard_orientation()
81
+ coords3d_2 = geom2_copy.coords3d.copy()
82
+
83
+ # After bringing the Geometries into standard orientation we may
84
+ # still have to consider additional axis swaps and reflections to
85
+ # allow optimal atom matching using the Hungarian method.
86
+
87
+ # Six possible axis swaps, (3*2, (x, y, z)*((x), (y), (z))).
88
+ axes = (0, 1, 2)
89
+ swaps = list(it.permutations(axes))
90
+ # Eight possible reflections, (±x, ±y, ±z).
91
+ reflections = (
92
+ ( 1, 1, 1), # no reflection
93
+ (-1, 1, 1), # reflect on yz plane
94
+ ( 1, -1, 1), # reflect on xz plane
95
+ ( 1, 1, -1), # reflect on xy plane
96
+ (-1, -1, 1), # reflect on yz and xz planes
97
+ (-1, 1, -1), # reflect on yz and xy planes
98
+ ( 1, -1, -1), # reflect on xz and xy planes
99
+ (-1, -1, -1) # reflect on yz, xz, xy planes
100
+ )
101
+ # 48 combinations of six axis swaps and eight reflections.
102
+ transforms = list(it.product(swaps, reflections))
103
+
104
+ matched_rmsds = list()
105
+ matched_coords = list()
106
+ for i, transform in enumerate(transforms):
107
+ # Apply swap and reflection
108
+ c3d_trans = apply_transform(coords3d_2.copy(), *transform)
109
+ geom2_to_match = geom2.copy()
110
+ geom2_to_match.coords3d = c3d_trans
111
+ # Apply Hungarian method to the transformed Geometry
112
+ geom2_matched = match_geom_atoms(geom1_copy, geom2_to_match)
113
+ mrmsd = rmsd.kabsch_rmsd(coords3d_1, geom2_matched.coords3d)
114
+ matched_rmsds.append(mrmsd)
115
+ matched_coords.append(geom2_matched.coords)
116
+
117
+ # Break when the two geometries are similar. Then we don't have to
118
+ # apply the remaining transformations.
119
+ if mrmsd <= thresh:
120
+ break
121
+
122
+ matched_rmsds = np.array(matched_rmsds)
123
+ min_rmsd_ind = matched_rmsds.argmin()
124
+ min_rmsd = matched_rmsds.min()
125
+ best_matching_coords = matched_coords[min_rmsd_ind]
126
+ geom2_copy.coords = best_matching_coords
127
+ return min_rmsd, (geom1_copy, geom2_copy)
pysisyphus/testing.py ADDED
@@ -0,0 +1,96 @@
1
+ import importlib
2
+ import shutil
3
+
4
+ from pysisyphus.config import Config, DEFAULTS
5
+ from pysisyphus import logger
6
+
7
+ try:
8
+ import pytest
9
+ except ModuleNotFoundError:
10
+ logger.warning("pytest package could not be imported.")
11
+
12
+
13
+ """Inspired by
14
+ https://github.com/MolSSI/QCEngine/blob/master/qcengine/testing.py
15
+
16
+ Maybe implement something like this to make test access easier
17
+ https://stackoverflow.com/a/36601576/12486216
18
+ """
19
+
20
+
21
+ _reason = "Calculator {} is not available."
22
+ _using_cache = dict()
23
+ # Python modules to be imported using importlib.import_module
24
+ IMPORT_DICT = {
25
+ "dftd4": "dftd4",
26
+ "pyscf": "pyscf",
27
+ "dalton": "daltonproject",
28
+ "qcengine": "qcengine",
29
+ "openmm": "simtk.openmm",
30
+ "thermoanalysis": "thermoanalysis",
31
+ "pyxtb": "xtb.interface",
32
+ "obabel": "openbabel.openbabel",
33
+ "geodesic": "geodesic_interpolate",
34
+ }
35
+
36
+
37
+ def module_available(calculator):
38
+ try:
39
+ import_name = IMPORT_DICT[calculator]
40
+ except KeyError:
41
+ return False
42
+
43
+ try:
44
+ _ = importlib.import_module(import_name)
45
+ available = True
46
+ except (ModuleNotFoundError, ImportError):
47
+ available = False
48
+ return available
49
+
50
+
51
+ class DummyMark:
52
+ def __init__(self, available):
53
+ self.args = (available,)
54
+
55
+
56
+ def using(calculator, set_pytest_mark=True):
57
+ """Calling disabling set_pytest_mark avoids a runtime dependency on pytest."""
58
+ calculator = calculator.lower()
59
+
60
+ if calculator not in _using_cache:
61
+ available = False
62
+
63
+ # Look into .pysisyphusrc first
64
+ try:
65
+ cmd = Config[calculator]["cmd"]
66
+ except KeyError:
67
+ # Try defaults last
68
+ try:
69
+ cmd = DEFAULTS[calculator]
70
+ except KeyError:
71
+ cmd = None
72
+
73
+ # Look for dscf availability
74
+ if calculator == "turbomole":
75
+ cmd = "dscf"
76
+
77
+ # Check if cmd is available on $PATH
78
+ if cmd is not None:
79
+ available = bool(shutil.which(cmd))
80
+ # Handling native python packages from here
81
+ else:
82
+ available = module_available(calculator)
83
+
84
+ reason = _reason.format(calculator)
85
+ skipif = not available
86
+ if set_pytest_mark:
87
+ mark = pytest.mark.skipif(skipif, reason=reason)
88
+ else:
89
+ mark = DummyMark(skipif)
90
+ _using_cache[calculator] = mark
91
+ return _using_cache[calculator]
92
+
93
+
94
+ def available(calculator, **kwargs):
95
+ # True when skipif is False
96
+ return not using(calculator, **kwargs).args[0]
pysisyphus/thermo.py ADDED
@@ -0,0 +1,156 @@
1
+ import h5py
2
+ import jinja2
3
+
4
+ try:
5
+ from thermoanalysis.QCData import QCData
6
+ from thermoanalysis.thermo import thermochemistry
7
+
8
+ can_thermoanalysis = True
9
+ except ModuleNotFoundError:
10
+ can_thermoanalysis = False
11
+
12
+ from pysisyphus.config import p_DEFAULT, T_DEFAULT
13
+ from pysisyphus.constants import AU2KJPERMOL, AU2KCALPERMOL
14
+ from pysisyphus.helpers_pure import highlight_text
15
+ from pysisyphus.Geometry import Geometry
16
+
17
+
18
+ def get_thermoanalysis_from_hess_h5(
19
+ h5_fn, T=T_DEFAULT, p=p_DEFAULT, point_group="c1", return_geom=False
20
+ ):
21
+ with h5py.File(h5_fn, "r") as handle:
22
+ masses = handle["masses"][:]
23
+ vibfreqs = handle["vibfreqs"][:]
24
+ coords3d = handle["coords3d"][:]
25
+ energy = handle.attrs["energy"]
26
+ mult = handle.attrs["mult"]
27
+ atoms = handle.attrs["atoms"]
28
+
29
+ thermo_dict = {
30
+ "masses": masses,
31
+ "wavenumbers": vibfreqs,
32
+ "coords3d": coords3d,
33
+ "scf_energy": energy,
34
+ "mult": mult,
35
+ }
36
+
37
+ qcd = QCData(thermo_dict, point_group=point_group)
38
+ thermo = thermochemistry(qcd, temperature=T, pressure=p)
39
+ if return_geom:
40
+ geom = Geometry(atoms=atoms, coords=coords3d)
41
+ return thermo, geom
42
+ else:
43
+ return thermo
44
+
45
+
46
+ THERMO_TPL = jinja2.Template(
47
+ """
48
+ {% if geom -%}
49
+ Geometry : {{ geom }}, {{ geom.atoms|length }} atoms
50
+ {%- endif %}
51
+ Temperature : {{ "%0.2f" % thermo.T }} K
52
+ Pressure : {{ thermo.p }} Pa
53
+ Total Mass : {{ "%0.4f" % thermo.M }} amu
54
+
55
+ ! Symmetry is currently not supported in pysisyphus. !
56
+ ! If not given otherwise, c1 and σ = 1 are assumed. !
57
+ Point Group : {{ thermo.point_group }}
58
+ Symmetry Number σ : {{ thermo.sym_num }}
59
+ Linear : {{ thermo.linear }}
60
+
61
+ +
62
+ | Normal Mode Wavenumbers
63
+ {{ sep }}
64
+ {% for nu in used_nus -%}
65
+ {{ "\t%04d" % loop.index }}: {{ nu }}
66
+ {% endfor -%}
67
+ {{ sep }}
68
+ {% if is_ts %}This should be a TS.{% endif %}
69
+ Expected {{ expected }} normal modes, got {{ used_nus|length}}.
70
+
71
+ +
72
+ | Inner energy U = U_el + U_vib + U_rot + U_trans
73
+ {{ sep }}
74
+ {{ fmt("U_el", thermo.U_el) }}
75
+ {{ fmt("ZPE", thermo.ZPE) }}
76
+ {{ fmt("U_vib (incl. ZPE)", thermo.U_vib) }}
77
+ {{ fmt("U_rot", thermo.U_rot) }}
78
+ {{ fmt("U_trans", thermo.U_trans) }}
79
+ {{ sep }}
80
+ {{ fmt("U", thermo.U_tot) }}
81
+
82
+ +
83
+ | Enthalpy H = U + kB*T
84
+ {{ sep }}
85
+ {{ fmt("U", thermo.U_tot) }}
86
+ {{ fmt("kB*T", thermo.kBT) }}
87
+ {{ sep }}
88
+ {{ fmt("H", thermo.H) }}
89
+
90
+ +
91
+ | Entropy correction T*S = T*(S_el + S_vib + S_rot + S_trans)
92
+ {{ sep }}
93
+ {{ fmt("T*S_el", thermo.TS_el) }}
94
+ {{ fmt("T*S_vib", thermo.TS_vib) }}
95
+ {{ fmt("T*S_rot", thermo.TS_rot) }}
96
+ {{ fmt("T*S_trans", thermo.TS_trans) }}
97
+ {{ sep }}
98
+ {{ fmt("T*S", thermo.TS_tot) }}
99
+
100
+ +
101
+ | Gibbs free energy G = H - T*S
102
+ {{ sep }}
103
+ {{ fmt("H", thermo.H) }}
104
+ {{ fmt("T*S", thermo.TS_tot) }}
105
+ {{ sep }}
106
+ {{ fmt("G", thermo.G) }}
107
+ {{ fmt("dG", thermo.dG) }}
108
+ """.strip()
109
+ )
110
+
111
+
112
+ def print_thermoanalysis(
113
+ thermo, geom=None, is_ts=False, unit="joule", level=0, title=None
114
+ ):
115
+ """Print thermochemical analysis."""
116
+
117
+ units = {
118
+ "calorie": ("kcal mol⁻¹", AU2KCALPERMOL),
119
+ "joule": ("kJ mol⁻¹", AU2KJPERMOL),
120
+ }
121
+ unit_key, unit_conv = units[unit]
122
+
123
+ def fmt(key, hartree):
124
+ """Output key & energy in Hartree and the chosen unit."""
125
+ kjm = hartree * unit_conv
126
+ return f"{key:<18}: {hartree: >16.8f} Eh ({kjm: >18.2f} {unit_key})"
127
+
128
+ # Separator
129
+ sep = "+-----------------------------------------------------------------+"
130
+
131
+ sub_modes = 5 if thermo.linear else 6
132
+ # Expect one real mode less if it is a TS
133
+ sub_modes += 1 if is_ts else 0
134
+ expected = 3 * thermo.atom_num - sub_modes
135
+
136
+ def fmt_nus(nus):
137
+ return [f"{nu: >8.2f} cm⁻¹" + (", excluded" if nu < 0.0 else "") for nu in nus]
138
+
139
+ rendered = THERMO_TPL.render(
140
+ geom=geom,
141
+ thermo=thermo,
142
+ org_nus=thermo.org_wavenumbers,
143
+ used_nus=fmt_nus(thermo.wavenumbers),
144
+ expected=expected,
145
+ is_ts=is_ts,
146
+ sep=sep,
147
+ fmt=fmt,
148
+ )
149
+
150
+ if title is None:
151
+ title = ""
152
+ else:
153
+ title = f", {title}"
154
+ print(highlight_text(f"Thermochemical corrections{title}", level=level))
155
+ print()
156
+ print(rendered)