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,893 @@
1
+ import glob
2
+ import io
3
+ from math import sqrt
4
+ from pathlib import Path
5
+ import os
6
+ import re
7
+ import struct
8
+ import shutil
9
+ import warnings
10
+
11
+ import numpy as np
12
+ import pyparsing as pp
13
+
14
+ from pysisyphus.calculators.OverlapCalculator import OverlapCalculator
15
+ from pysisyphus.constants import BOHR2ANG, ANG2BOHR
16
+ from pysisyphus.helpers_pure import file_or_str
17
+ from pysisyphus.wavefunction import norm_ci_coeffs, Wavefunction
18
+
19
+
20
+ def make_sym_mat(table_block):
21
+ mat_size = int(table_block[1])
22
+ # Orca prints blocks of 5 columns
23
+ arr = np.array(table_block[2:], dtype=float)
24
+ assert arr.size == mat_size**2
25
+ block_size = 5 * mat_size
26
+ cbs = [
27
+ arr[i * block_size : (i + 1) * block_size].reshape(mat_size, -1)
28
+ for i in range(arr.size // block_size + 1)
29
+ ]
30
+ return np.concatenate(cbs, axis=1)
31
+
32
+
33
+ def save_orca_pc_file(point_charges, pc_fn, hardness=None):
34
+ point_charges = point_charges.copy()
35
+ # ORCA excepcts point charge positions in Angstrom
36
+ point_charges[:, :3] *= BOHR2ANG
37
+
38
+ # ORCA also expects the ordering <q> <x> <y> <z>, so we have to resort.
39
+ shape = point_charges.shape
40
+ if hardness is not None:
41
+ shape = shape[0], shape[1] + 1
42
+ point_charges_orca = np.zeros_like(point_charges)
43
+ point_charges_orca = np.zeros(shape)
44
+ point_charges_orca[:, 0] = point_charges[:, 3]
45
+ point_charges_orca[:, 1:4] = point_charges[:, :3]
46
+
47
+ if hardness:
48
+ point_charges_orca[:, 4] = hardness
49
+ np.savetxt(
50
+ pc_fn,
51
+ point_charges_orca,
52
+ fmt="%16.10f",
53
+ header=str(len(point_charges)),
54
+ comments="",
55
+ )
56
+
57
+
58
+ def parse_orca_gbw(gbw_fn):
59
+ """Adapted from
60
+ https://orcaforum.kofo.mpg.de/viewtopic.php?f=8&t=3299
61
+
62
+ The first 5 long int values represent pointers into the file:
63
+
64
+ Pointer @+0: Internal ORCA data structures
65
+ Pointer @+8: Geometry
66
+ Pointer @+16: BasisSet
67
+ Pointer @+24: Orbitals
68
+ Pointer @+32: ECP data
69
+ """
70
+
71
+ with open(gbw_fn, "rb") as handle:
72
+ handle.seek(24)
73
+ offset = struct.unpack("<q", handle.read(8))[0]
74
+ handle.seek(offset)
75
+ operators = struct.unpack("<i", handle.read(4))[0]
76
+ dimension = struct.unpack("<i", handle.read(4))[0]
77
+
78
+ coeffs_fmt = "<" + dimension**2 * "d"
79
+ assert operators == 1, "Unrestricted case is not implemented!"
80
+
81
+ for i in range(operators):
82
+ # print('\nOperator: {}'.format(i))
83
+ coeffs = struct.unpack(coeffs_fmt, handle.read(8 * dimension**2))
84
+ occupations = struct.iter_unpack("<d", handle.read(8 * dimension))
85
+ energies = struct.iter_unpack("<d", handle.read(8 * dimension))
86
+ irreps = struct.iter_unpack("<i", handle.read(4 * dimension))
87
+ cores = struct.iter_unpack("<i", handle.read(4 * dimension))
88
+
89
+ coeffs = np.array(coeffs).reshape(-1, dimension)
90
+ energies = np.array([en[0] for en in energies])
91
+
92
+ # MOs are returned in columns
93
+ return coeffs, energies
94
+
95
+
96
+ def parse_orca_cis(cis_fn):
97
+ """
98
+ Read binary CI vector file from ORCA.
99
+ Loosly based on TheoDORE 1.7.1, Authors: S. Mai, F. Plasser
100
+ https://sourceforge.net/p/theodore-qc
101
+ """
102
+ cis_handle = open(cis_fn, "rb")
103
+ # self.log(f"Parsing CI vectors from {cis_handle}")
104
+
105
+ # The header consists of 9 4-byte integers, the first 5 of which give useful info.
106
+ nvec = struct.unpack("i", cis_handle.read(4))[0]
107
+ # [0] index of first alpha occ, is equal to number of frozen alphas
108
+ # [1] index of last alpha occ
109
+ # [2] index of first alpha virt
110
+ # [3] index of last alpha virt, header[3]+1 is equal to number of bfs
111
+ # [4] index of first beta occ, for restricted equal to -1
112
+ # [5] index of last beta occ, for restricted equal to -1
113
+ # [6] index of first beta virt, for restricted equal to -1
114
+ # [7] index of last beta virt, for restricted equal to -1
115
+ header = [struct.unpack("i", cis_handle.read(4))[0] for i in range(8)]
116
+
117
+ def parse_header(header):
118
+ first_occ, last_occ, first_virt, last_virt = header
119
+ frozen = first_occ
120
+ occupied = last_occ + 1
121
+ active = occupied - frozen
122
+ mo_num = last_virt + 1
123
+ virtual = mo_num - first_virt
124
+ return frozen, active, occupied, virtual
125
+
126
+ a_frozen, a_active, a_occupied, a_virtual = parse_header(header[:4])
127
+ b_header = parse_header(header[4:])
128
+ unrestricted = all([bh != -1 for bh in (b_header)])
129
+ b_frozen, b_active, b_occupied, b_virtual = b_header
130
+ a_lenci = a_active * a_virtual
131
+ b_lenci = b_active * b_virtual
132
+ a_nel = a_frozen + a_active
133
+ b_nel = b_frozen + b_active
134
+ if not unrestricted:
135
+ b_nel = a_nel
136
+ # expect_mult = a_nel - b_nel + 1
137
+
138
+ # Loop over states. For non-TDA order is: X+Y of 1, X-Y of 1,
139
+ # X+Y of 2, X-Y of 2, ...
140
+ prev_root = -1
141
+ prev_mult = None
142
+ iroot_triplets = 0
143
+
144
+ # Flags that may later be set to True
145
+ triplets = False
146
+ spin_flip = False
147
+ tda = False
148
+ Xs_a = list()
149
+ Ys_a = list()
150
+ Xs_b = list()
151
+ Ys_b = list()
152
+
153
+ def parse_coeffs(lenci, frozen, occupied, virtual):
154
+ coeffs = struct.unpack(lenci * "d", cis_handle.read(lenci * 8))
155
+ coeffs = np.array(coeffs).reshape(-1, virtual)
156
+ # create full array, i.e nocc x nvirt
157
+ coeffs_full = np.zeros((occupied, virtual))
158
+ coeffs_full[frozen:] = coeffs
159
+ return coeffs_full
160
+
161
+ def handle_X_Y(root_updated, Xs, Ys, coeffs):
162
+ # When the root was not incremented compared to the previous root we have
163
+ # just parsed X-Y (and parsed X+Y before.)
164
+ #
165
+ # We can recover the separate X and Y vectors by first computing X as
166
+ # X = (X + Y + X - Y) / 2
167
+ # and then
168
+ # Y = X + Y - X
169
+ if root_updated:
170
+ X_plus_Y = Xs[-1] # Parsed in previous cycle
171
+ X_minus_Y = coeffs # Parsed in current cycle
172
+ X = 0.5 * (X_plus_Y + X_minus_Y)
173
+ Y = X_plus_Y - X
174
+ # Update the X and Y vectors that were already saved with their correct values.
175
+ Xs[-1] = X
176
+ Ys[-1] = Y
177
+ # When the root was incremented we either have a TDA calculation without Y or
178
+ # we parsed X-Y in the previous cycle and now moved to a new root.
179
+ else:
180
+ Xs.append(coeffs)
181
+ Ys.append(np.zeros_like(coeffs))
182
+
183
+ for ivec in range(nvec):
184
+ # Header of each vector, contains 6 4-byte ints.
185
+ ncoeffs, _, mult, _, iroot, _ = struct.unpack("iiiiii", cis_handle.read(24))
186
+
187
+ # Check if we deal with a spin-flip calculation. There, excitations are from
188
+ # α_activate -> β_virtual.
189
+ if unrestricted and ncoeffs == (a_active * b_virtual):
190
+ unrestricted = False # Don't expect β_active -> β_virtual.
191
+ spin_flip = True
192
+ a_lenci = ncoeffs
193
+ a_virtual = b_virtual
194
+ warnings.warn(
195
+ "Spin-flip calculation detected. Pysisyphus can parse it, but "
196
+ "the transition density matrix is not yet handled properly by the "
197
+ "OverlapCalculator-class or the Wavefunction-class!"
198
+ )
199
+
200
+ if prev_mult is None:
201
+ prev_mult = mult
202
+
203
+ # 2 x 8 bytes unknown?!
204
+ ene, _ = struct.unpack("dd", cis_handle.read(16))
205
+
206
+ # Will evaluate True only once when triplets were requested.
207
+ if prev_mult != mult:
208
+ triplets = True
209
+ prev_root = -1
210
+
211
+ # When we encounter the second "state" we can decide if it is a TDA
212
+ # calculation (without Y-vector).
213
+ if (ivec == 1) and (iroot == prev_root + 1):
214
+ tda = True
215
+
216
+ if triplets:
217
+ iroot = iroot_triplets
218
+
219
+ root_updated = prev_root == iroot
220
+
221
+ # self.log(f"{ivec=}, {nele=}, {mult=}, {iroot=}, {root_updated=}")
222
+ # Then come nact * nvirt 8-byte doubles with the coefficients
223
+ coeffs_a = parse_coeffs(a_lenci, a_frozen, a_occupied, a_virtual)
224
+ handle_X_Y(root_updated, Xs_a, Ys_a, coeffs_a)
225
+ if unrestricted:
226
+ coeffs_b = parse_coeffs(b_lenci, b_frozen, b_occupied, b_virtual)
227
+ handle_X_Y(root_updated, Xs_b, Ys_b, coeffs_b)
228
+
229
+ # Somehow ORCA stops to update iroot correctly after the singlet states.
230
+ if (mult == 3) and (tda or (ivec % 2) == 1):
231
+ iroot_triplets += 1
232
+
233
+ prev_root = iroot
234
+ prev_mult = mult
235
+ # Verify, that we are at the EOF. We request 1 byte, but we only get 0.
236
+ assert len(cis_handle.read(1)) == 0
237
+ cis_handle.close()
238
+
239
+ # Convert everything to numpy arrays.
240
+ Xs_a, Ys_a, Xs_b, Ys_b = [np.array(_) for _ in (Xs_a, Ys_a, Xs_b, Ys_b)]
241
+
242
+ def handle_triplets(Xs, Ys):
243
+ assert (len(Xs) % 2) == 0
244
+ states = len(Xs) // 2
245
+ Xs = Xs[states:]
246
+ Ys = Ys[states:]
247
+ return Xs, Ys
248
+
249
+ # Only return triplet states if present
250
+ if triplets:
251
+ Xs_a, Ys_a = handle_triplets(Xs_a, Ys_a)
252
+ assert len(Xs_b) == 0
253
+ assert len(Ys_b) == 0
254
+
255
+ # Beta part will be empty
256
+ if not unrestricted:
257
+ assert len(Xs_b) == 0
258
+ assert len(Ys_b) == 0
259
+ Xs_b = np.zeros_like(Xs_a)
260
+ Ys_b = np.zeros_like(Xs_b)
261
+
262
+ return Xs_a, Ys_a, Xs_b, Ys_b
263
+
264
+
265
+ @file_or_str(".log", ".out")
266
+ def parse_orca_all_energies(text, triplets=False, do_tddft=False):
267
+ energy_re = r"FINAL SINGLE POINT ENERGY\s*([-\.\d]+)"
268
+ energy_mobj = re.search(energy_re, text)
269
+ gs_energy = float(energy_mobj.groups()[0])
270
+ all_energies = [gs_energy]
271
+
272
+ if do_tddft:
273
+ scf_re = re.compile(r"E\(SCF\)\s+=\s*([\d\-\.]+) Eh")
274
+ scf_mobj = scf_re.search(text)
275
+ scf_en = float(scf_mobj.group(1))
276
+ gs_energy = scf_en
277
+ tddft_re = re.compile(r"STATE\s*(\d+):\s*E=\s*([\d\.]+)\s*au")
278
+ states, exc_ens = zip(
279
+ *[(int(state), float(en)) for state, en in tddft_re.findall(text)]
280
+ )
281
+ if triplets:
282
+ roots = len(states) // 2
283
+ exc_ens = exc_ens[-roots:]
284
+ states = states[-roots:]
285
+ assert len(exc_ens) == len(set(states))
286
+ all_energies = np.full(1 + len(exc_ens), gs_energy)
287
+ all_energies[1:] += exc_ens
288
+ all_energies = np.array(all_energies)
289
+ return all_energies
290
+
291
+
292
+ def get_name(text: bytes):
293
+ """Return string that comes before first \x00 character & offset."""
294
+ until = text.find(b"\x00")
295
+ return text[:until].decode(), until
296
+
297
+
298
+ @file_or_str(".densities", mode="rb")
299
+ def parse_orca_densities(text: bytes):
300
+ handle = io.BytesIO(text)
301
+
302
+ # Determine file size
303
+ handle.seek(0, 2)
304
+ file_size = handle.tell()
305
+ handle.seek(0, 0)
306
+
307
+ offset, _ = struct.unpack(
308
+ "ii", handle.read(8)
309
+ ) # Don't know about the second integer
310
+ # print("offset", offset)
311
+ dens_size = offset - 8
312
+ assert dens_size % 8 == 0
313
+ dens_floats = dens_size // 8
314
+ # print(f"Expecting {dens_floats} density doubles")
315
+ densities = struct.unpack("d" * dens_floats, handle.read(dens_size))
316
+ ndens = struct.unpack("i", handle.read(4))[0]
317
+
318
+ # Block of 512 bytes meta data. I don't really know what is contained in there.
319
+ meta = handle.read(512)
320
+ base_name, _ = get_name(meta)
321
+
322
+ # Now multiple 512 byte blocks for each density follow
323
+ dens_names = list()
324
+ for i in range(ndens):
325
+ dens_meta = handle.read(512)
326
+ dens_name, _ = get_name(dens_meta)
327
+ dens_names.append(dens_name)
328
+ # don't know about the first item, 2nd items seems to 0
329
+ _, _, nao1, nao2 = struct.unpack("iiii", handle.read(16))
330
+ assert nao1 == nao2
331
+ _ = struct.unpack("b", handle.read(1))[0] # 0 byte
332
+ assert _ == 0
333
+ dens_exts = [Path(dens_name).suffix[1:] for dens_name in dens_names]
334
+
335
+ # Verify that we parsed to whole file
336
+ assert file_size - handle.tell() == 0
337
+ handle.close()
338
+
339
+ # Construct density matrices
340
+ assert dens_floats % ndens == 0
341
+ nao = int(sqrt(dens_floats // ndens))
342
+ dens_shape = (nao, nao)
343
+ densities = np.array(densities).reshape(ndens, *dens_shape)
344
+
345
+ dens_dict = {dens_ext: dens for dens_ext, dens in zip(dens_exts, densities)}
346
+ # This check could be removed but I'll keep if for now, so I only deal with
347
+ # known densities.
348
+ # scfp : HF/DFT electronic density
349
+ # scfr : HF/DFT spin density
350
+ # cisp : TDA/TD-DFT/CIS electronic density
351
+ # cisr : TDA/TD-DFT/CIS spin density
352
+ assert set(dens_dict) <= {"scfp", "scfr", "cisp", "cisr"}
353
+
354
+ return dens_dict
355
+
356
+
357
+ def get_exc_ens_fosc(wf_fn, cis_fn, log_fn):
358
+ wf = Wavefunction.from_file(wf_fn)
359
+ Xa, Ya, Xb, Yb = parse_orca_cis(cis_fn)
360
+ all_energies = parse_orca_all_energies(log_fn, do_tddft=True)
361
+ Xa, Ya = norm_ci_coeffs(Xa, Ya)
362
+ exc_ens = all_energies[1:] - all_energies[0]
363
+ tdens = wf.get_transition_dipole_moment(Xa + Ya)
364
+ warnings.warn("Only alpha TDM is currently taken into account!")
365
+ fosc = wf.oscillator_strength(exc_ens, tdens)
366
+ return exc_ens, fosc
367
+
368
+
369
+ class ORCA(OverlapCalculator):
370
+
371
+ conf_key = "orca"
372
+ _set_plans = (
373
+ "gbw",
374
+ "out",
375
+ "cis",
376
+ "densities",
377
+ ("molden", "mwfn_wf"),
378
+ )
379
+
380
+ def __init__(
381
+ self,
382
+ keywords,
383
+ blocks="",
384
+ gbw=None,
385
+ do_stable=False,
386
+ numfreq=False,
387
+ json_dump=True,
388
+ **kwargs,
389
+ ):
390
+ """ORCA calculator.
391
+
392
+ Wrapper for creating ORCA input files for energy, gradient
393
+ and Hessian calculations. The PAL and memory inputs must not
394
+ be given in the keywords and/or blocks, as they are handled
395
+ by the 'pal' and 'memory' arguments.
396
+
397
+ Parameters
398
+ ----------
399
+ keywords : str
400
+ Keyword line, as normally given in ORCA, excluding the
401
+ leading "!".
402
+ blocks : str, optional
403
+ ORCA block input(s), e.g. for TD-DFT calculations (%tddft ... end).
404
+ As the blocks start with a leading "%", wrapping the input in quotes
405
+ ("") is required, otherwise the parsing will fail.
406
+ gbw : str, optional
407
+ Path to an input gbw file, which will be used as initial guess
408
+ for the first calculation. Will be overriden later, with the
409
+ path to the gbw file of a previous calculation.
410
+ do_stable: bool, optional
411
+ Run stability analysis until a stable wavefunction is obtained,
412
+ before every calculation.
413
+ numfreq : bool, optional
414
+ Use numerical frequencies instead of analytical ones.
415
+ json_dump : bool, optional
416
+ Whether to dump the wavefunction to JSON via orca_2json. The JSON can become
417
+ very large in calculations comprising many basis functions.
418
+ """
419
+ super().__init__(**kwargs)
420
+
421
+ self.keywords = keywords.lower()
422
+ self.blocks = blocks.lower()
423
+ self.gbw = gbw
424
+ self.do_stable = bool(do_stable)
425
+ self.freq_keyword = "numfreq" if numfreq else "freq"
426
+ self.json_dump = bool(json_dump)
427
+
428
+ assert ("pal" not in keywords) and ("nprocs" not in blocks), (
429
+ "PALn/nprocs not " "allowed! Use 'pal: n' in the 'calc' section instead."
430
+ )
431
+ assert "maxcore" not in blocks, (
432
+ "maxcore not allowed! " "Use 'mem: n' in the 'calc' section instead!"
433
+ )
434
+
435
+ self.to_keep = (
436
+ "inp",
437
+ "out:orca.out",
438
+ "gbw",
439
+ "engrad",
440
+ "hessian",
441
+ "cis",
442
+ "molden:orca.molden",
443
+ "hess",
444
+ "pcgrad",
445
+ "densities:orca.densities",
446
+ "json",
447
+ )
448
+ self.do_tddft = False
449
+ if "tddft" in self.blocks:
450
+ self.do_tddft = True
451
+ try:
452
+ self.root = int(re.search(r"iroot\s*(\d+)", self.blocks).group(1))
453
+ except AttributeError:
454
+ self.log("Doing TDA/TDDFT calculation without gradient.")
455
+ self.triplets = bool(re.search(r"triplets\s+true", self.blocks))
456
+ self.inp_fn = "orca.inp"
457
+ self.out_fn = "orca.out"
458
+
459
+ self.orca_input = """!{keywords} {calc_type}
460
+ {moinp}
461
+
462
+ %pal nprocs {pal} end
463
+ %maxcore {mem}
464
+
465
+ {blocks}
466
+ {pointcharges}
467
+
468
+ *xyz {charge} {mult}
469
+ {coords}
470
+ *
471
+ """
472
+
473
+ self.parser_funcs = {
474
+ "energy": self.parse_energy,
475
+ "grad": self.parse_engrad,
476
+ "hessian": self.parse_hessian,
477
+ "noparse": lambda path: None,
478
+ "stable": self.parse_stable,
479
+ }
480
+
481
+ self.base_cmd = self.get_cmd()
482
+
483
+ def reattach(self, last_calc_cycle):
484
+ # Use the latest .gbw
485
+ gbw = self.make_fn("gbw", last_calc_cycle)
486
+ self.log(f"Restarted. using {gbw}")
487
+
488
+ def get_moinp_str(self, gbw):
489
+ moinp_str = ""
490
+ if gbw:
491
+ moinp_str = f"""!moread
492
+ %moinp "{os.path.abspath(gbw)}" """
493
+ return moinp_str
494
+
495
+ def prepare_input(
496
+ self, atoms, coords, calc_type, point_charges=None, do_stable=False
497
+ ):
498
+ coords = self.prepare_coords(atoms, coords)
499
+ if self.gbw:
500
+ self.log(f"Using {self.gbw}")
501
+ else:
502
+ self.log("Using initial guess provided by ORCA")
503
+ if calc_type == "noparse":
504
+ calc_type = ""
505
+
506
+ pc_str = ""
507
+ if point_charges is not None:
508
+ pc_fn = self.make_fn("pointcharges_inp.pc")
509
+ save_orca_pc_file(point_charges, pc_fn)
510
+ pc_str = f'%pointcharges "{pc_fn}"'
511
+ stable_block = "\n%scf stabperform true hftyp uhf end" if do_stable else ""
512
+ blocks = self.get_block_str() + stable_block
513
+
514
+ inp = self.orca_input.format(
515
+ keywords=self.keywords,
516
+ calc_type=calc_type,
517
+ moinp=self.get_moinp_str(self.gbw),
518
+ pal=self.pal,
519
+ mem=self.mem,
520
+ blocks=blocks,
521
+ pointcharges=pc_str,
522
+ coords=coords,
523
+ charge=self.charge,
524
+ mult=self.mult,
525
+ )
526
+ return inp
527
+
528
+ def get_block_str(self):
529
+ block_str = self.blocks
530
+ # Use the correct root if we track it
531
+ if self.track:
532
+ block_str = re.sub(r"iroot\s+(\d+)", f"iroot {self.root}", self.blocks)
533
+ self.log(f"Using iroot '{self.root}' for excited state gradient.")
534
+ return block_str
535
+
536
+ def get_stable_wavefunction(self, atoms, coords):
537
+ self.log("Trying to get a stable wavefunction")
538
+
539
+ stable = False
540
+ max_cycles = 10
541
+ for i in range(max_cycles):
542
+ inp = self.prepare_input(atoms, coords, calc_type="", do_stable=True)
543
+ stable = self.run(inp, calc="stable")
544
+ self.log(f"{i:02d} stable: {stable}")
545
+
546
+ if stable:
547
+ self.log(f"Found stable wavefunction in cycle {i}!")
548
+ break
549
+ else:
550
+ raise Exception(
551
+ "Could not find stable wavefunction in {max_cycles}! " "Aborting."
552
+ )
553
+
554
+ def parse_stable(self, path):
555
+ with open(path / "orca.out") as handle:
556
+ text = handle.read()
557
+
558
+ stable_re = re.compile("Stability Analysis indicates a stable")
559
+ stable = bool(stable_re.search(text))
560
+
561
+ unstable_re = re.compile("Stability Analysis indicates an UNSTABLE")
562
+ unstable = bool(unstable_re.search(text))
563
+
564
+ stable = stable and not unstable
565
+
566
+ return stable
567
+
568
+ def store_and_track(self, results, func, atoms, coords, **prepare_kwargs):
569
+ if self.track:
570
+ self.store_overlap_data(atoms, coords)
571
+ if self.track_root():
572
+ # Redo the calculation with the updated root
573
+ results = func(atoms, coords, **prepare_kwargs)
574
+ results["all_energies"] = self.parse_all_energies()
575
+ return results
576
+
577
+ def get_energy(self, atoms, coords, **prepare_kwargs):
578
+ calc_type = ""
579
+
580
+ if self.do_stable:
581
+ self.get_stable_wavefunction(atoms, coords)
582
+
583
+ inp = self.prepare_input(atoms, coords, calc_type, **prepare_kwargs)
584
+ results = self.run(inp, calc="energy")
585
+ results = self.store_and_track(
586
+ results, self.get_energy, atoms, coords, **prepare_kwargs
587
+ )
588
+ return results
589
+
590
+ def get_forces(self, atoms, coords, **prepare_kwargs):
591
+ if self.do_stable:
592
+ self.get_stable_wavefunction(atoms, coords)
593
+
594
+ calc_type = "engrad"
595
+ inp = self.prepare_input(atoms, coords, calc_type, **prepare_kwargs)
596
+ kwargs = {
597
+ "calc": "grad",
598
+ }
599
+ results = self.run(inp, **kwargs)
600
+ results = self.store_and_track(
601
+ results, self.get_forces, atoms, coords, **prepare_kwargs
602
+ )
603
+ return results
604
+
605
+ def get_hessian(self, atoms, coords, **prepare_kwargs):
606
+ calc_type = self.freq_keyword
607
+
608
+ if self.do_stable:
609
+ self.get_stable_wavefunction(atoms, coords)
610
+
611
+ inp = self.prepare_input(atoms, coords, calc_type, **prepare_kwargs)
612
+ results = self.run(inp, calc="hessian")
613
+ # results = self.store_and_track(
614
+ # results, self.get_hessian, atoms, coords, **prepare_kwargs
615
+ # )
616
+ return results
617
+
618
+ def run_calculation(self, atoms, coords, **prepare_kwargs):
619
+ """Basically some kind of dummy method that can be called
620
+ to execute ORCA with the stored cmd of this calculator."""
621
+ inp = self.prepare_input(atoms, coords, "noparse", **prepare_kwargs)
622
+ kwargs = {
623
+ "calc": "energy",
624
+ }
625
+ results = self.run(inp, **kwargs)
626
+ if self.track:
627
+ self.store_overlap_data(atoms, coords)
628
+ return results
629
+
630
+ def run_after(self, path):
631
+ # Create .molden file when CDDs are requested
632
+ if self.cdds:
633
+ cmd = "orca_2mkl orca -molden"
634
+ self.popen(cmd, cwd=path)
635
+ shutil.copy(path / "orca.molden.input", path / "orca.molden")
636
+
637
+ if self.json_dump:
638
+ # Will silently fail with ECPs
639
+ cmd = "orca_2json orca"
640
+ proc = self.popen(cmd, cwd=path)
641
+ if (ret := proc.returncode) != 0:
642
+ self.log(f"orca_2json call failed with return-code {ret}!")
643
+
644
+ @staticmethod
645
+ @file_or_str(".hess", method=False)
646
+ def parse_hess_file(text):
647
+ integer = pp.Word(pp.nums)
648
+ float_ = pp.Word(pp.nums + ".-")
649
+ plus = pp.Literal("+")
650
+ minus = pp.Literal("-")
651
+ E = pp.Literal("E")
652
+ scientific = pp.Combine(float_ + E + pp.Or([plus, minus]) + integer)
653
+
654
+ table_header_line = pp.Suppress(integer + pp.restOfLine)
655
+ scientific_line = pp.Suppress(integer) + pp.OneOrMore(scientific)
656
+ scientific_block = table_header_line + pp.OneOrMore(scientific_line)
657
+ float_line = pp.Suppress(integer) + float_
658
+ comment_line = pp.Literal("#") + pp.restOfLine
659
+ mass_xyz_line = pp.Group(
660
+ pp.Word(pp.alphas) + float_ + pp.Group(pp.OneOrMore(float_))
661
+ )
662
+
663
+ block_name = pp.Word(pp.alphas + "$_")
664
+ block_length = integer
665
+
666
+ block_int = block_name + block_length
667
+ block_float = block_name + float_
668
+ block_table = block_name + integer + pp.OneOrMore(scientific_block)
669
+ block_table_two_int = (
670
+ block_name + integer + pp.Suppress(integer) + pp.OneOrMore(scientific_block)
671
+ )
672
+ block_float_table = block_name + integer + pp.OneOrMore(float_line)
673
+ block_atoms = block_name + integer + pp.OneOrMore(mass_xyz_line)
674
+
675
+ act_atom = block_int.setResultsName("act_atom")
676
+ act_coord = block_int.setResultsName("act_coord")
677
+ act_energy = block_float.setResultsName("act_energy")
678
+ hessian = block_table.setResultsName("hessian")
679
+ vib_freqs = block_float_table.setResultsName("vib_freqs")
680
+ normal_modes = block_table_two_int.setResultsName("normal_modes")
681
+ atoms = block_atoms.setResultsName("atoms")
682
+
683
+ parser = (
684
+ block_name
685
+ + act_atom
686
+ + act_coord
687
+ + act_energy
688
+ + hessian
689
+ + vib_freqs
690
+ + normal_modes
691
+ + pp.OneOrMore(comment_line)
692
+ + atoms
693
+ )
694
+ parsed = parser.parseString(text)
695
+ return parsed
696
+
697
+ def parse_hessian(self, path):
698
+ hessian_fn = glob.glob(os.path.join(path, "*.hess"))
699
+ assert len(hessian_fn) == 1
700
+ hessian_fn = hessian_fn[0]
701
+ if not hessian_fn:
702
+ raise Exception("ORCA calculation failed.")
703
+
704
+ parsed = ORCA.parse_hess_file(hessian_fn)
705
+
706
+ # logging.warning("Hacky orca energy parsing in orca hessian calculation!")
707
+ orca_log_fn = os.path.join(path, self.out_fn)
708
+ with open(orca_log_fn) as handle:
709
+ log_text = handle.read()
710
+
711
+ energy_re = r"FINAL SINGLE POINT ENERGY\s*([-\.\d]+)"
712
+ energy_mobj = re.search(energy_re, log_text)
713
+ energy = float(energy_mobj.groups()[0])
714
+
715
+ results = {
716
+ "energy": energy,
717
+ "hessian": make_sym_mat(parsed["hessian"]),
718
+ }
719
+
720
+ return results
721
+
722
+ def parse_energy(self, path):
723
+ log_fn = glob.glob(os.path.join(path / "orca.out"))
724
+ if not log_fn:
725
+ raise Exception("ORCA calculation failed.")
726
+
727
+ assert len(log_fn) == 1
728
+ log_fn = log_fn[0]
729
+ with open(log_fn) as handle:
730
+ text = handle.read()
731
+ mobj = re.search(r"FINAL SINGLE POINT ENERGY\s+([\d\-\.]+)", text)
732
+ energy = float(mobj[1])
733
+ return {"energy": energy}
734
+
735
+ def parse_engrad(self, path):
736
+ results = {}
737
+ engrad_fn = glob.glob(os.path.join(path, "*.engrad"))
738
+ if not engrad_fn:
739
+ raise Exception("ORCA calculation failed.")
740
+
741
+ assert len(engrad_fn) == 1
742
+ engrad_fn = engrad_fn[0]
743
+ with open(engrad_fn) as handle:
744
+ engrad = handle.read()
745
+ engrad = re.findall(r"([\d\-\.]+)", engrad)
746
+ atoms = int(engrad.pop(0))
747
+ energy = float(engrad.pop(0))
748
+ force = -np.array(engrad[: 3 * atoms], dtype=float)
749
+ results["energy"] = energy
750
+ results["forces"] = force
751
+
752
+ return results
753
+
754
+ @staticmethod
755
+ def parse_cis(cis):
756
+ """Simple wrapper of external function.
757
+
758
+ Currently, only returns Xα and Yα.
759
+ """
760
+ return parse_orca_cis(cis)[:2]
761
+
762
+ @staticmethod
763
+ def parse_gbw(gbw_fn):
764
+ return parse_orca_gbw(gbw_fn)
765
+
766
+ @staticmethod
767
+ def set_mo_coeffs_in_gbw(in_gbw_fn, out_gbw_fn, mo_coeffs):
768
+ """See self.parse_gbw."""
769
+
770
+ with open(in_gbw_fn, "rb") as handle:
771
+ handle.seek(24)
772
+ offset = struct.unpack("<q", handle.read(8))[0]
773
+ handle.seek(offset)
774
+ operators = struct.unpack("<i", handle.read(4))[0]
775
+ dimension = struct.unpack("<i", handle.read(4))[0]
776
+ assert operators == 1, "Unrestricted case is not implemented!"
777
+
778
+ handle.seek(0)
779
+ gbw_bytes = handle.read()
780
+
781
+ tot_offset = offset + 4 + 4
782
+ start = gbw_bytes[:tot_offset]
783
+ end = gbw_bytes[tot_offset + 8 * dimension**2 :]
784
+ # Construct new gbw content by replacing the MO coefficients in the middle
785
+ mod_gbw_bytes = start + mo_coeffs.T.tobytes() + end
786
+
787
+ with open(out_gbw_fn, "wb") as handle:
788
+ handle.write(mod_gbw_bytes)
789
+
790
+ def parse_all_energies(self, text=None, triplets=None):
791
+ if text is None:
792
+ with open(self.out) as handle:
793
+ text = handle.read()
794
+
795
+ if triplets is None:
796
+ triplets = self.triplets
797
+
798
+ return parse_orca_all_energies(text, triplets, self.do_tddft)
799
+
800
+ @staticmethod
801
+ @file_or_str(".out", method=False)
802
+ def parse_atoms_coords(text):
803
+ ac_re = re.compile(
804
+ r"CARTESIAN COORDINATES \(ANGSTROEM\)\s+\-{33}(.+?)\s+\-{28}", re.DOTALL
805
+ )
806
+ mobj = ac_re.search(text)
807
+ atoms_coords = mobj.group(1).strip().split()
808
+ # atoms, *coords = np.array(atoms_coords).reshape(-1, 4).T
809
+ atoms_coords = np.array(atoms_coords).reshape(-1, 4)
810
+ atoms = tuple(atoms_coords[:, 0])
811
+ coords = atoms_coords[:, 1:].astype(float).flatten() * ANG2BOHR
812
+ return atoms, coords
813
+
814
+ @staticmethod
815
+ @file_or_str(".out", method=False)
816
+ def parse_engrad_info(text):
817
+ soi_re = re.compile(r"State of interest\s+\.{3}\s+(\d+)")
818
+ try:
819
+ root = soi_re.search(text).group(1)
820
+ root = int(root)
821
+ except AttributeError:
822
+ root = None
823
+ triplets = bool(re.search(r"triplets\s+true", text))
824
+ return root, triplets
825
+
826
+ def parse_mo_numbers(self, out_fn):
827
+ with open(out_fn) as handle:
828
+ text = handle.read()
829
+ electron_re = r"NEL\s*....\s*(\d+)"
830
+ electrons = int(re.search(electron_re, text)[1])
831
+ assert electrons % 2 == 0, "unrestricted is not yet supported!"
832
+ occ_num = int(electrons / 2)
833
+
834
+ mo_re = r"Dim\s*....\s*(\d+)"
835
+ mo_num = int(re.search(mo_re, text)[1])
836
+ virt_num = mo_num - occ_num
837
+ self.log(
838
+ f"found {electrons} electrons, {mo_num} MOs, with "
839
+ f"{occ_num} occupied and {virt_num} virtual."
840
+ )
841
+ return occ_num, virt_num
842
+
843
+ def set_mo_coeffs(self, mo_coeffs=None, gbw=None):
844
+ if mo_coeffs is not None:
845
+ self.mo_coeffs = mo_coeffs
846
+ return
847
+ if not gbw and self.gbw:
848
+ gbw = self.gbw
849
+ else:
850
+ raise Exception("Got no .gbw file to parse!")
851
+ self.log(f"Setting MO coefficients from {gbw}.")
852
+ self.mo_coeffs, _ = self.parse_gbw(self.gbw)
853
+
854
+ def prepare_overlap_data(self, path):
855
+ # Parse eigenvectors from tda/tddft calculation
856
+ X, Y = self.parse_cis(self.cis)
857
+ # Parse mo coefficients from gbw file and write a 'fake' turbomole
858
+ # mos file.
859
+ C, _ = self.parse_gbw(self.gbw)
860
+ all_energies = self.parse_all_energies()
861
+ return C, X, Y, all_energies
862
+
863
+ def get_chkfiles(self):
864
+ return {
865
+ "gbw": self.gbw,
866
+ }
867
+
868
+ def set_chkfiles(self, chkfiles):
869
+ try:
870
+ gbw = chkfiles["gbw"]
871
+ self.gbw = gbw
872
+ self.log(f"Set chkfile '{gbw}' as gbw.")
873
+ except KeyError:
874
+ self.log("Found no gbw information in chkfiles!")
875
+
876
+ @file_or_str(".out", method=True)
877
+ def check_termination(self, text):
878
+ term_re = re.compile(r"\*{4}ORCA TERMINATED NORMALLY\*{4}")
879
+ mobj = term_re.search(text)
880
+ return bool(mobj)
881
+
882
+ def clean_tmp(self, path):
883
+ tmp_fns = path.glob("*.tmp")
884
+ for tmp in tmp_fns:
885
+ os.remove(tmp)
886
+ self.log(f"Removed '{tmp}'")
887
+ # try:
888
+ # os.remove(path / "orca.gbw")
889
+ # except FileNotFoundError:
890
+ # pass
891
+
892
+ def __str__(self):
893
+ return f"ORCA({self.name})"