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,140 @@
1
+ import warnings
2
+
3
+ import numpy as np
4
+ from ase.calculators.calculator import Calculator
5
+ from ase.constraints import FixAtoms, FixBondLengths
6
+
7
+
8
+ class HarmonicFixAtoms(Calculator):
9
+ """
10
+ Harmonic position restraint on a subset of atoms.
11
+
12
+ E = 1/2 * k_fix * sum_i |r_i - r_i^ref|^2
13
+ """
14
+
15
+ implemented_properties = ["energy", "forces"]
16
+
17
+ def __init__(self, indices, ref_positions, k_fix=300.0):
18
+ super().__init__()
19
+ idx = np.asarray(indices, dtype=int).ravel()
20
+ if idx.size == 0:
21
+ raise ValueError("HarmonicFixAtoms requires at least one index.")
22
+ ref_pos = np.asarray(ref_positions, dtype=float)
23
+ if ref_pos.shape != (idx.size, 3):
24
+ raise ValueError(
25
+ f"ref_positions must have shape ({idx.size}, 3), got {ref_pos.shape}"
26
+ )
27
+ self.indices = idx
28
+ self.ref_positions = ref_pos
29
+ self.k_fix = float(k_fix)
30
+
31
+ def calculate(self, atoms, properties, system_changes):
32
+ super().calculate(atoms, properties, system_changes)
33
+ pos = atoms.get_positions().astype(float)
34
+ disp = pos[self.indices] - self.ref_positions
35
+ energy = 0.5 * self.k_fix * np.sum(disp ** 2)
36
+ forces = np.zeros_like(pos, dtype=float)
37
+ forces[self.indices] = -self.k_fix * disp
38
+ self.results = {
39
+ "energy": float(energy),
40
+ "forces": forces,
41
+ }
42
+
43
+
44
+ class HarmonicFixBondLengths(Calculator):
45
+ """
46
+ Harmonic bond-length restraint for selected atom pairs.
47
+
48
+ E = 1/2 * k_bond * sum_ij (d_ij - d_ij^ref)^2
49
+ """
50
+
51
+ implemented_properties = ["energy", "forces"]
52
+
53
+ def __init__(self, pairs, ref_distances, k_bond=300.0):
54
+ super().__init__()
55
+ pairs = np.asarray(pairs, dtype=int)
56
+ if pairs.ndim != 2 or pairs.shape[1] != 2:
57
+ raise ValueError("pairs must have shape (n_pairs, 2)")
58
+ ref_distances = np.asarray(ref_distances, dtype=float).ravel()
59
+ if ref_distances.shape[0] != pairs.shape[0]:
60
+ raise ValueError(
61
+ "ref_distances must have same length as pairs"
62
+ )
63
+ self.pairs = pairs
64
+ self.ref_distances = ref_distances
65
+ self.k_bond = float(k_bond)
66
+
67
+ def calculate(self, atoms, properties, system_changes):
68
+ super().calculate(atoms, properties, system_changes)
69
+ pos = atoms.get_positions().astype(float)
70
+ i_idx = self.pairs[:, 0]
71
+ j_idx = self.pairs[:, 1]
72
+ rij = pos[i_idx] - pos[j_idx]
73
+ dist = np.linalg.norm(rij, axis=1)
74
+ diff = dist - self.ref_distances
75
+ energy = 0.5 * self.k_bond * np.sum(diff ** 2)
76
+ inv_dist = np.zeros_like(dist)
77
+ nonzero = dist > 0.0
78
+ inv_dist[nonzero] = 1.0 / dist[nonzero]
79
+ force_scale = -self.k_bond * diff * inv_dist
80
+ fij = (force_scale[:, None] * rij).astype(float)
81
+ forces = np.zeros_like(pos, dtype=float)
82
+ np.add.at(forces, i_idx, fij)
83
+ np.add.at(forces, j_idx, -fij)
84
+ self.results = {
85
+ "energy": float(energy),
86
+ "forces": forces,
87
+ }
88
+
89
+
90
+ def _iter_constraints(atoms):
91
+ constraints = getattr(atoms, "constraints", None)
92
+ if constraints is None:
93
+ return []
94
+ if isinstance(constraints, (list, tuple)):
95
+ return list(constraints)
96
+ return [constraints]
97
+
98
+
99
+ def build_harmonic_constraint_calculators(atoms, k_fix=300.0, k_bond=300.0):
100
+ """
101
+ Build harmonic-restraint calculators from ASE constraints.
102
+
103
+ Supported constraints:
104
+ - FixAtoms
105
+ - FixBondLength / FixBondLengths
106
+ """
107
+
108
+ calculators = []
109
+ constraints = _iter_constraints(atoms)
110
+ remaining_constraints = []
111
+ for constraint in constraints:
112
+ if isinstance(constraint, FixAtoms):
113
+ indices = np.asarray(constraint.index, dtype=int)
114
+ ref_positions = atoms.get_positions()[indices]
115
+ calculators.append(
116
+ HarmonicFixAtoms(indices, ref_positions, k_fix=k_fix)
117
+ )
118
+ elif isinstance(constraint, FixBondLengths):
119
+ pairs = np.asarray(constraint.pairs, dtype=int)
120
+ if constraint.bondlengths is None:
121
+ pos = atoms.get_positions()
122
+ rij = pos[pairs[:, 0]] - pos[pairs[:, 1]]
123
+ ref_distances = np.linalg.norm(rij, axis=1)
124
+ else:
125
+ ref_distances = np.asarray(constraint.bondlengths, dtype=float)
126
+ calculators.append(
127
+ HarmonicFixBondLengths(pairs, ref_distances, k_bond=k_bond)
128
+ )
129
+ remaining_constraints.append(constraint)
130
+ else:
131
+ warnings.warn(
132
+ f"Unsupported ASE constraint {type(constraint).__name__}; "
133
+ "ignored for harmonic restraints."
134
+ )
135
+ remaining_constraints.append(constraint)
136
+ if remaining_constraints != constraints:
137
+ atoms.set_constraint(remaining_constraints or None)
138
+ return calculators
139
+
140
+
mlmm/hessian_cache.py ADDED
@@ -0,0 +1,44 @@
1
+ """In-process Hessian cache for the ``all`` workflow.
2
+
3
+ Stores Hessian matrices as CPU tensors (or numpy arrays) in a module-level
4
+ dict so that subsequent CLI stages executed via ``_run_cli_main()`` can
5
+ reuse them without recomputation.
6
+
7
+ Each entry may carry ``active_dofs`` — a list of DOF indices that the stored
8
+ (partial) Hessian spans. Consumers set ``geometry.within_partial_hessian``
9
+ before assigning partial Hessians to ``geometry.cart_hessian``.
10
+ """
11
+
12
+ import numpy as np
13
+ import torch
14
+ from typing import Any, Dict, Optional, Sequence
15
+
16
+ _cache: Dict[str, Any] = {}
17
+
18
+
19
+ def store(
20
+ key: str,
21
+ H,
22
+ active_dofs: Optional[Sequence[int]] = None,
23
+ meta: Optional[dict] = None,
24
+ ) -> None:
25
+ """Cache a (possibly partial) Hessian on CPU."""
26
+ if isinstance(H, torch.Tensor):
27
+ h_cpu = H.detach().cpu().clone()
28
+ else:
29
+ h_cpu = np.array(H, copy=True)
30
+ _cache[key] = {
31
+ "hessian": h_cpu,
32
+ "active_dofs": list(active_dofs) if active_dofs is not None else None,
33
+ "meta": meta or {},
34
+ }
35
+
36
+
37
+ def load(key: str) -> Optional[Dict[str, Any]]:
38
+ """Return cached entry or *None*."""
39
+ return _cache.get(key)
40
+
41
+
42
+ def clear() -> None:
43
+ """Drop all cached Hessians."""
44
+ _cache.clear()
mlmm/hessian_calc.py ADDED
@@ -0,0 +1,174 @@
1
+ """Hessian utilities and vibrational analysis helpers for the ML/MM workflow.
2
+
3
+ These routines construct Hessians, compute frequencies and handle I/O.
4
+ """
5
+
6
+ import os
7
+ from contextlib import nullcontext
8
+ import numpy as np
9
+ import time
10
+ import torch
11
+ from typing import Sequence
12
+ from ase.data import atomic_masses
13
+ from ase.constraints import FixAtoms
14
+ from ase.atoms import Atoms
15
+ from pysisyphus.constants import AMU2AU
16
+
17
+
18
+ def clone_atoms(atoms):
19
+ """Return a shallow copy of *atoms* keeping constraints (no PBC)."""
20
+ new = Atoms(
21
+ symbols=atoms.get_chemical_symbols(),
22
+ positions=atoms.get_positions(),
23
+ pbc=False,
24
+ )
25
+
26
+ if atoms.constraints:
27
+ fix_idx = [
28
+ i for c in atoms.constraints if isinstance(c, FixAtoms)
29
+ for i in c.get_indices()
30
+ ]
31
+ if fix_idx:
32
+ new.set_constraint(FixAtoms(indices=fix_idx))
33
+
34
+ return new
35
+
36
+
37
+ def hessian_calc(
38
+ atoms,
39
+ calc,
40
+ delta: float = 0.01,
41
+ info_path: str | None = None,
42
+ *,
43
+ dtype: np.dtype = np.float64,
44
+ ):
45
+ """
46
+ Numerically compute the full Cartesian Hessian (second derivatives).
47
+
48
+ Finite differences are taken by displacing each *unfixed* atom by
49
+ ``±delta`` Å along x, y, z. Central differences give the sub-Hessian,
50
+ which is **always expanded to the full (3N, 3N) matrix** by padding
51
+ zeros for fixed atoms before returning.
52
+
53
+ Parameters
54
+ ----------
55
+ atoms : ase.Atoms
56
+ Structure to differentiate.
57
+ calc : ase.Calculator
58
+ Calculator providing forces.
59
+ delta : float, default 0.01 Å
60
+ Displacement size.
61
+ info_path : str | None
62
+ If given, progress info is appended to this file.
63
+ dtype : numpy dtype, default float64
64
+ Data type of the returned Hessian.
65
+
66
+ Returns
67
+ -------
68
+ ndarray
69
+ Cartesian Hessian of shape ``(3N, 3N)`` where
70
+ ``N == len(atoms)`` (fixed-atom rows/cols are zero).
71
+ """
72
+ fixed = {
73
+ i for c in atoms.constraints if isinstance(c, FixAtoms)
74
+ for i in c.get_indices()
75
+ }
76
+ movable = np.asarray([i for i in range(len(atoms)) if i not in fixed])
77
+ m = len(movable)
78
+ n_dof = 3 * m
79
+
80
+ # Short-circuit: all atoms frozen → return zeros
81
+ if m == 0:
82
+ N = len(atoms)
83
+ return np.zeros((3 * N, 3 * N))
84
+
85
+ H_sub = np.empty((n_dof, n_dof), dtype=dtype)
86
+ row = 0
87
+
88
+ log_cm = nullcontext(None)
89
+ if info_path is not None:
90
+ os.makedirs(os.path.dirname(info_path), exist_ok=True)
91
+ log_cm = open(info_path, "w", encoding="utf-8")
92
+
93
+ with log_cm as log:
94
+ if info_path is not None:
95
+ log.write("Hessian calculation by numerical differentiation\n")
96
+ log.write("------------------------------------------------\n")
97
+ t0 = time.time()
98
+ checkpoints = [int(m * k / 10) for k in range(1, 11)] # every 10 %
99
+
100
+ for count, a in enumerate(movable, start=1):
101
+ for coord in range(3):
102
+ plus = clone_atoms(atoms)
103
+ plus.calc = calc
104
+ plus.positions[a, coord] += delta
105
+ Fp = plus.calc.get_forces(plus)
106
+
107
+ minus = clone_atoms(atoms)
108
+ minus.calc = calc
109
+ minus.positions[a, coord] -= delta
110
+ Fm = minus.calc.get_forces(minus)
111
+
112
+ # Central difference: −∂F/∂x = ∂²E/∂x²
113
+ H_sub[row] = (Fm - Fp)[movable].ravel() / (2 * delta)
114
+ row += 1
115
+
116
+ if info_path is not None and count in checkpoints:
117
+ pct = checkpoints.index(count) * 10 + 10
118
+ elapsed = time.time() - t0
119
+ total_est = elapsed * 100.0 / pct
120
+ remaining = total_est - elapsed
121
+ me, se = divmod(int(elapsed), 60)
122
+ mr, sr = divmod(int(remaining), 60)
123
+ log.write(
124
+ f"{pct:3d}% ({count}/{m}) Elapsed {me}m{se}s ETA {mr}m{sr}s\n"
125
+ )
126
+ log.flush()
127
+
128
+ if info_path is not None:
129
+ elapsed = time.time() - t0
130
+ me, se = divmod(int(elapsed), 60)
131
+ log.write(f"Done in {me}m{se}s\n")
132
+
133
+ N = len(atoms)
134
+ H = np.zeros((3 * N, 3 * N), dtype=dtype)
135
+ for i_local, i_atom in enumerate(movable):
136
+ for j_local, j_atom in enumerate(movable):
137
+ H[
138
+ 3 * i_atom : 3 * i_atom + 3,
139
+ 3 * j_atom : 3 * j_atom + 3,
140
+ ] = H_sub[
141
+ 3 * i_local : 3 * i_local + 3,
142
+ 3 * j_local : 3 * j_local + 3,
143
+ ]
144
+ return H
145
+
146
+
147
+ def _get_masses(Z):
148
+ """Return atomic masses (amu) for a list/array of atomic numbers."""
149
+ return np.array([atomic_masses[z] for z in Z])
150
+
151
+
152
+ def _build_tr_basis(coords_bohr: torch.Tensor, masses_amu: torch.Tensor) -> torch.Tensor:
153
+ """Return (3N,6) mass-weighted basis for Tx,Ty,Tz,Rx,Ry,Rz."""
154
+ device, dtype = coords_bohr.device, coords_bohr.dtype
155
+ N = coords_bohr.shape[0]
156
+
157
+ m_au = masses_amu.to(dtype=dtype, device=device) * AMU2AU
158
+ m_sqrt = torch.sqrt(m_au).reshape(-1, 1)
159
+
160
+ com = (m_au.reshape(-1, 1) * coords_bohr).sum(0) / m_au.sum()
161
+ x = coords_bohr - com
162
+
163
+ eye3 = torch.eye(3, dtype=dtype, device=device)
164
+ cols = []
165
+
166
+ for i in range(3): # Tx, Ty, Tz
167
+ cols.append((eye3[i].repeat(N, 1) * m_sqrt).reshape(-1, 1))
168
+ for i in range(3): # Rx, Ry, Rz
169
+ rot = torch.cross(x, eye3[i].expand_as(x), dim=1) * m_sqrt
170
+ cols.append(rot.reshape(-1, 1))
171
+
172
+ return torch.cat(cols, dim=1)
173
+
174
+