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,349 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import shutil
5
+ import sys
6
+ import importlib.util
7
+ from importlib import import_module
8
+ from pathlib import Path
9
+ from typing import Any, Dict, Optional
10
+
11
+ _EXT_CACHE: Dict[str, Optional[Any]] = {
12
+ "nonbonded": None,
13
+ "analytical_hessian": None,
14
+ "bonded": None,
15
+ }
16
+ _EXT_ERROR: Dict[str, Optional[str]] = {
17
+ "nonbonded": None,
18
+ "analytical_hessian": None,
19
+ "bonded": None,
20
+ }
21
+
22
+
23
+ def _rebuild_hint() -> str:
24
+ return (
25
+ "mlmm-toolkit JIT-compiles C++ extensions on first use via torch.utils.cpp_extension.\n"
26
+ "This requires a C++ compiler (g++). If compilation fails, run: apt install g++ (or: yum install gcc-c++)\n"
27
+ "To manually rebuild:\n"
28
+ " cd $(python -c \"import hessian_ff; print(hessian_ff.__path__[0])\")/native && make clean && make"
29
+ )
30
+
31
+
32
+ def _with_rebuild_hint(msg: str) -> str:
33
+ txt = str(msg).strip()
34
+ if not txt:
35
+ txt = "native extension unavailable"
36
+ return f"{txt}\n{_rebuild_hint()}"
37
+
38
+
39
+ def _ensure_max_jobs() -> None:
40
+ """Set Ninja parallel compile jobs if not provided by user.
41
+
42
+ This reduces first-build wall time for native extensions.
43
+ """
44
+ if "MAX_JOBS" in os.environ:
45
+ return
46
+ ncpu = os.cpu_count()
47
+ if ncpu is None or ncpu < 1:
48
+ return
49
+ os.environ["MAX_JOBS"] = str(int(ncpu))
50
+
51
+
52
+ def try_load_native_backend(module_name: str = "hessian_ff_native") -> Optional[Any]:
53
+ """Try importing a native extension backend module.
54
+
55
+ Returns the module object if available, otherwise ``None``.
56
+ """
57
+ try:
58
+ return import_module(module_name)
59
+ except Exception:
60
+ return None
61
+
62
+
63
+ def native_backend_status(module_name: str = "hessian_ff_native") -> Dict[str, str]:
64
+ """Return a short status dict for native backend availability."""
65
+ mod = try_load_native_backend(module_name=module_name)
66
+ if mod is None:
67
+ return {
68
+ "available": "false",
69
+ "module": module_name,
70
+ "backend": "native_required",
71
+ "note": "native extension module is not loaded",
72
+ }
73
+ return {
74
+ "available": "true",
75
+ "module": module_name,
76
+ "backend": "native",
77
+ "note": "native extension is loaded",
78
+ }
79
+
80
+
81
+ def _cache_build_dir(build_subdir: str) -> Path:
82
+ """Return the user-cache fallback build directory.
83
+
84
+ Used when the package-internal directory (site-packages) is read-only.
85
+ Location: $XDG_CACHE_HOME/mlmm-toolkit/hessian_ff/<build_subdir>
86
+ """
87
+ cache_root = Path(
88
+ os.environ.get("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
89
+ )
90
+ return cache_root / "mlmm-toolkit" / "hessian_ff" / build_subdir
91
+
92
+
93
+ def _build_in_tree_extension(
94
+ *,
95
+ key: str,
96
+ ext_name: str,
97
+ source_files: list[str],
98
+ build_subdir: str,
99
+ verbose: bool,
100
+ force_rebuild: bool,
101
+ ) -> Optional[Any]:
102
+ if _EXT_CACHE[key] is not None and not force_rebuild:
103
+ return _EXT_CACHE[key]
104
+ if _EXT_ERROR[key] is not None and not force_rebuild:
105
+ return None
106
+
107
+ here = Path(__file__).resolve().parent
108
+ # Candidate build directories: package-internal first, then user cache.
109
+ pkg_build_dir = here / build_subdir
110
+ cache_build_dir = _cache_build_dir(build_subdir)
111
+ build_dirs = [pkg_build_dir, cache_build_dir]
112
+
113
+ def _find_prebuilt_so() -> Optional[Path]:
114
+ """Search all candidate directories for a prebuilt .so."""
115
+ for bd in build_dirs:
116
+ if not bd.is_dir():
117
+ continue
118
+ cands = sorted(
119
+ bd.glob(f"{ext_name}*.so"),
120
+ key=lambda p: p.stat().st_mtime,
121
+ reverse=True,
122
+ )
123
+ if cands:
124
+ return cands[0]
125
+ return None
126
+
127
+ def _load_prebuilt_so(path: Path) -> Optional[Any]:
128
+ try:
129
+ mod_name = path.stem
130
+ if mod_name in sys.modules:
131
+ mod = sys.modules[mod_name]
132
+ _EXT_CACHE[key] = mod
133
+ _EXT_ERROR[key] = None
134
+ return mod
135
+ spec = importlib.util.spec_from_file_location(mod_name, str(path))
136
+ if spec is None or spec.loader is None:
137
+ raise ImportError(f"spec loader is unavailable for {path}")
138
+ mod = importlib.util.module_from_spec(spec)
139
+ spec.loader.exec_module(mod)
140
+ _EXT_CACHE[key] = mod
141
+ _EXT_ERROR[key] = None
142
+ return mod
143
+ except Exception as e:
144
+ _EXT_ERROR[key] = _with_rebuild_hint(
145
+ f"failed to load prebuilt extension {path}: {e}"
146
+ )
147
+ return None
148
+
149
+ # Prefer prebuilt artifact when available (useful on nodes without a compiler toolchain).
150
+ if not force_rebuild:
151
+ prebuilt = _find_prebuilt_so()
152
+ if prebuilt is not None:
153
+ loaded = _load_prebuilt_so(prebuilt)
154
+ if loaded is not None:
155
+ return loaded
156
+
157
+ try:
158
+ from torch.utils.cpp_extension import load
159
+ except Exception as e:
160
+ _EXT_ERROR[key] = _with_rebuild_hint(
161
+ f"torch cpp_extension import failed: {e}"
162
+ )
163
+ return None
164
+
165
+ _ensure_max_jobs()
166
+
167
+ srcs = [here / s for s in source_files]
168
+
169
+ def _try_build_in_dir(
170
+ build_dir: Path,
171
+ extra_cflags: list[str],
172
+ extra_ldflags: Optional[list[str]] = None,
173
+ ):
174
+ os.makedirs(build_dir, exist_ok=True)
175
+ kwargs = dict(
176
+ name=ext_name,
177
+ sources=[str(s) for s in srcs],
178
+ extra_cflags=extra_cflags,
179
+ extra_ldflags=(extra_ldflags or []),
180
+ build_directory=str(build_dir),
181
+ verbose=bool(verbose),
182
+ )
183
+ # Some cluster conda envs do not provide ninja. Prefer distutils build
184
+ # in that case instead of failing hard.
185
+ if shutil.which("ninja") is None:
186
+ kwargs["use_ninja"] = False
187
+ try:
188
+ return load(**kwargs)
189
+ except TypeError:
190
+ kwargs.pop("use_ninja", None)
191
+ return load(**kwargs)
192
+
193
+ # Try aggressive CPU flags first, then fall back progressively.
194
+ build_attempts = [
195
+ (["-Ofast", "-ffast-math", "-funroll-loops", "-march=native", "-mtune=native", "-fopenmp"], ["-fopenmp"]),
196
+ (["-O3", "-ffast-math", "-funroll-loops", "-march=native", "-mtune=native", "-fopenmp"], ["-fopenmp"]),
197
+ (["-O3", "-ffast-math", "-funroll-loops", "-fopenmp"], ["-fopenmp"]),
198
+ (["-O3", "-fopenmp"], ["-fopenmp"]),
199
+ (["-O3"], []),
200
+ ]
201
+
202
+ # Try each build directory: package-internal first, then user cache.
203
+ last_err: Optional[Exception] = None
204
+ for build_dir in build_dirs:
205
+ try:
206
+ os.makedirs(build_dir, exist_ok=True)
207
+ except OSError:
208
+ continue
209
+ for cflags, ldflags in build_attempts:
210
+ try:
211
+ _EXT_CACHE[key] = _try_build_in_dir(
212
+ build_dir,
213
+ extra_cflags=cflags,
214
+ extra_ldflags=ldflags,
215
+ )
216
+ _EXT_ERROR[key] = None
217
+ return _EXT_CACHE[key]
218
+ except Exception as e:
219
+ last_err = e
220
+ continue
221
+ # All flag combinations failed in this directory; try next.
222
+
223
+ _EXT_CACHE[key] = None
224
+ _EXT_ERROR[key] = _with_rebuild_hint(
225
+ f"hessian_ff build attempts failed: {last_err}"
226
+ )
227
+ return None
228
+
229
+
230
+ def get_nonbonded_extension(
231
+ *,
232
+ verbose: bool = False,
233
+ force_rebuild: bool = False,
234
+ ) -> Optional[Any]:
235
+ """Build/load in-tree C++ extension for nonbonded kernels.
236
+
237
+ References:
238
+ - torch.utils.cpp_extension.load() runtime build workflow.
239
+ """
240
+ return _build_in_tree_extension(
241
+ key="nonbonded",
242
+ ext_name="hessian_ff_nonbonded_ext",
243
+ source_files=["nonbonded_ext.cpp"],
244
+ build_subdir=".build_nonbonded",
245
+ verbose=bool(verbose),
246
+ force_rebuild=bool(force_rebuild),
247
+ )
248
+
249
+
250
+ def nonbonded_extension_status() -> Dict[str, str]:
251
+ ext = get_nonbonded_extension(verbose=False, force_rebuild=False)
252
+ if ext is None:
253
+ note = _EXT_ERROR["nonbonded"] or _with_rebuild_hint("extension unavailable")
254
+ return {
255
+ "available": "false",
256
+ "backend": "native_required",
257
+ "note": note,
258
+ }
259
+ return {
260
+ "available": "true",
261
+ "backend": "native_nonbonded_cpp",
262
+ "note": "nonbonded extension loaded",
263
+ }
264
+
265
+
266
+ def get_analytical_hessian_extension(
267
+ *,
268
+ verbose: bool = False,
269
+ force_rebuild: bool = False,
270
+ ) -> Optional[Any]:
271
+ """Build/load in-tree C++ extension for analytical Hessian helpers."""
272
+ return _build_in_tree_extension(
273
+ key="analytical_hessian",
274
+ ext_name="hessian_ff_analytical_hessian_ext",
275
+ source_files=["analytical_hessian_ext.cpp"],
276
+ build_subdir=".build_analytical_hessian",
277
+ verbose=bool(verbose),
278
+ force_rebuild=bool(force_rebuild),
279
+ )
280
+
281
+
282
+ def analytical_hessian_extension_status() -> Dict[str, str]:
283
+ ext = get_analytical_hessian_extension(verbose=False, force_rebuild=False)
284
+ if ext is None:
285
+ note = _EXT_ERROR["analytical_hessian"] or _with_rebuild_hint("extension unavailable")
286
+ return {
287
+ "available": "false",
288
+ "backend": "native_analytical_hessian_optional",
289
+ "note": note,
290
+ }
291
+ return {
292
+ "available": "true",
293
+ "backend": "native_analytical_hessian_cpp",
294
+ "note": "analytical Hessian extension loaded",
295
+ }
296
+
297
+
298
+ def get_bonded_extension(
299
+ *,
300
+ verbose: bool = False,
301
+ force_rebuild: bool = False,
302
+ ) -> Optional[Any]:
303
+ """Build/load in-tree C++ extension for bonded energy-force kernels."""
304
+ return _build_in_tree_extension(
305
+ key="bonded",
306
+ ext_name="hessian_ff_bonded_ext",
307
+ source_files=["bonded_ext.cpp"],
308
+ build_subdir=".build_bonded",
309
+ verbose=bool(verbose),
310
+ force_rebuild=bool(force_rebuild),
311
+ )
312
+
313
+
314
+ def bonded_extension_status() -> Dict[str, str]:
315
+ ext = get_bonded_extension(verbose=False, force_rebuild=False)
316
+ if ext is None:
317
+ note = _EXT_ERROR["bonded"] or _with_rebuild_hint("extension unavailable")
318
+ return {
319
+ "available": "false",
320
+ "backend": "native_bonded_optional",
321
+ "note": note,
322
+ }
323
+ return {
324
+ "available": "true",
325
+ "backend": "native_bonded_cpp",
326
+ "note": "bonded extension loaded",
327
+ }
328
+
329
+
330
+ def build_native_extensions(
331
+ *,
332
+ verbose: bool = False,
333
+ force_rebuild: bool = False,
334
+ ) -> Dict[str, str]:
335
+ """Build/load all native extensions up front.
336
+
337
+ This provides a practical "compile together" workflow by triggering
338
+ all in-tree extension builds in one step before production runs.
339
+ """
340
+ ext_nb = get_nonbonded_extension(verbose=verbose, force_rebuild=force_rebuild)
341
+ ext_ah = get_analytical_hessian_extension(verbose=verbose, force_rebuild=force_rebuild)
342
+ ext_bd = get_bonded_extension(verbose=verbose, force_rebuild=force_rebuild)
343
+ return {
344
+ "nonbonded": "ok" if ext_nb is not None else f"error: {_EXT_ERROR['nonbonded']}",
345
+ "analytical_hessian": "ok"
346
+ if ext_ah is not None
347
+ else f"error: {_EXT_ERROR['analytical_hessian']}",
348
+ "bonded": "ok" if ext_bd is not None else f"error: {_EXT_ERROR['bonded']}",
349
+ }
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ import torch
4
+
5
+ from .loader import get_nonbonded_extension, nonbonded_extension_status
6
+
7
+
8
+ def nonbonded_energy_force_native(
9
+ *,
10
+ coords: torch.Tensor,
11
+ charge: torch.Tensor,
12
+ atom_type: torch.Tensor,
13
+ lj_acoef: torch.Tensor,
14
+ lj_bcoef: torch.Tensor,
15
+ hb_acoef: torch.Tensor,
16
+ hb_bcoef: torch.Tensor,
17
+ nb_index: torch.Tensor,
18
+ pair_i: torch.Tensor,
19
+ pair_j: torch.Tensor,
20
+ pair14_i: torch.Tensor,
21
+ pair14_j: torch.Tensor,
22
+ pair14_inv_scee: torch.Tensor,
23
+ pair14_inv_scnb: torch.Tensor,
24
+ cpu_fast: bool = True,
25
+ verbose: bool = False,
26
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
27
+ """Call C++ nonbonded extension.
28
+
29
+ Returns
30
+ -------
31
+ (E_coul, E_lj, E_coul14, E_lj14, force)
32
+
33
+ References:
34
+ - native/nonbonded_ext.cpp in this repository.
35
+ """
36
+ ext = get_nonbonded_extension(verbose=verbose, force_rebuild=False)
37
+ if ext is None:
38
+ status = nonbonded_extension_status()
39
+ raise RuntimeError(
40
+ "native nonbonded extension is unavailable; torch fallback is disabled. "
41
+ f"detail: {status.get('note')}"
42
+ )
43
+
44
+ chunk_size = max(int(pair_i.numel()), int(pair14_i.numel()), 1)
45
+ out = ext.nonbonded_energy_force(
46
+ coords,
47
+ charge,
48
+ atom_type,
49
+ lj_acoef,
50
+ lj_bcoef,
51
+ hb_acoef,
52
+ hb_bcoef,
53
+ nb_index,
54
+ pair_i,
55
+ pair_j,
56
+ pair14_i,
57
+ pair14_j,
58
+ pair14_inv_scee,
59
+ pair14_inv_scnb,
60
+ int(chunk_size),
61
+ bool(cpu_fast),
62
+ -1,
63
+ )
64
+ return out[0], out[1], out[2], out[3], out[4]
65
+
66
+
67
+ def nonbonded_energy_force_preparam_native(
68
+ *,
69
+ coords: torch.Tensor,
70
+ pair_i: torch.Tensor,
71
+ pair_j: torch.Tensor,
72
+ pair_coul_coeff: torch.Tensor,
73
+ pair_a12_coeff: torch.Tensor,
74
+ pair_b6_coeff: torch.Tensor,
75
+ pair_b10_coeff: torch.Tensor,
76
+ pair14_i: torch.Tensor,
77
+ pair14_j: torch.Tensor,
78
+ pair14_coul_coeff: torch.Tensor,
79
+ pair14_a12_coeff: torch.Tensor,
80
+ pair14_b6_coeff: torch.Tensor,
81
+ pair14_b10_coeff: torch.Tensor,
82
+ cpu_fast: bool = True,
83
+ verbose: bool = False,
84
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
85
+ """Call C++ nonbonded extension using precomputed pair coefficients.
86
+
87
+ Returns
88
+ -------
89
+ (E_coul, E_lj, E_coul14, E_lj14, force)
90
+ """
91
+ ext = get_nonbonded_extension(verbose=verbose, force_rebuild=False)
92
+ if ext is None:
93
+ status = nonbonded_extension_status()
94
+ raise RuntimeError(
95
+ "native nonbonded extension is unavailable; torch fallback is disabled. "
96
+ f"detail: {status.get('note')}"
97
+ )
98
+
99
+ chunk_size = max(int(pair_i.numel()), int(pair14_i.numel()), 1)
100
+ out = ext.nonbonded_energy_force_preparam(
101
+ coords,
102
+ pair_i,
103
+ pair_j,
104
+ pair_coul_coeff,
105
+ pair_a12_coeff,
106
+ pair_b6_coeff,
107
+ pair_b10_coeff,
108
+ pair14_i,
109
+ pair14_j,
110
+ pair14_coul_coeff,
111
+ pair14_a12_coeff,
112
+ pair14_b6_coeff,
113
+ pair14_b10_coeff,
114
+ int(chunk_size),
115
+ bool(cpu_fast),
116
+ -1,
117
+ )
118
+ return out[0], out[1], out[2], out[3], out[4]