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
pysisyphus/io/crd.py ADDED
@@ -0,0 +1,101 @@
1
+ import re
2
+
3
+ import jinja2
4
+ import numpy as np
5
+
6
+ from pysisyphus.constants import ANG2BOHR
7
+ from pysisyphus.Geometry import Geometry
8
+ from pysisyphus.helpers_pure import file_or_str
9
+
10
+
11
+ @file_or_str(".crd")
12
+ def parse_crd(crd):
13
+ lines = [l.strip() for l in crd.strip().split("\n") if not l.startswith("*")]
14
+ expect_line = lines.pop(0)
15
+ expect_num = int(expect_line.split()[0])
16
+ coords3d = np.zeros((expect_num, 3), dtype=float)
17
+ atoms = list()
18
+ re_ = re.compile(r"\d+")
19
+ for i, line in enumerate(lines):
20
+ _, _, res, atom, x, y, z, *_ = line.split()
21
+ coords3d[i] = (x, y, z)
22
+ atom = re_.sub("", atom) # Delte number
23
+ atoms.append(atom)
24
+ assert len(atoms) == expect_num
25
+ coords3d *= ANG2BOHR
26
+ return atoms, coords3d.flatten()
27
+
28
+
29
+ def geom_from_crd(fn, **kwargs):
30
+ atoms, coords = parse_crd(fn)
31
+ return Geometry(atoms, coords, **kwargs)
32
+
33
+
34
+ # I10 I10 2X A8 2X A8 3F20.10 2X A8 2X A8 F20.10
35
+ CRD_TPL = """* Created by pysisyphus
36
+ *
37
+ {{ i10(atomnum) }} EXT
38
+ {% for atom, x, y, z in atoms_xyz -%}
39
+ {{ i10(loop.index) }}{{ i10(resno) }} {{ a8(res) }} {{ a8(atom) }}{{ f20(x) }}{{ f20(y) }}{{ f20(z) }} {{ a8(segid) }} {{ a8(resid) }}{{ f20(0.0) }}
40
+ {% endfor %}
41
+ """
42
+
43
+
44
+ def i10(int_):
45
+ return f"{int_:10d}"
46
+
47
+
48
+ def f20(float_):
49
+ return f"{float_:>20.10f}"
50
+
51
+
52
+ def a8(str_):
53
+ return f"{str(str_):<8s}"
54
+
55
+
56
+ def atoms_coords_to_crd_str(
57
+ atoms,
58
+ coords,
59
+ resno=1,
60
+ res="UNL1",
61
+ segid=None,
62
+ resid=1,
63
+ ref_atoms=None,
64
+ del_atoms=None,
65
+ ):
66
+ if segid is None:
67
+ segid = res
68
+ if del_atoms is None:
69
+ del_atoms = []
70
+
71
+ env = jinja2.Environment(loader=jinja2.BaseLoader)
72
+ env.globals.update(i10=i10, f20=f20, a8=a8)
73
+ tpl = env.from_string(CRD_TPL)
74
+
75
+ if ref_atoms is not None:
76
+ atoms_for_names = ref_atoms
77
+ else:
78
+ atoms_for_names = atoms
79
+ counter = dict()
80
+ atom_names = list()
81
+ for atom in atoms_for_names:
82
+ counter.setdefault(atom, 1)
83
+ atom_names.append(f"{atom}{counter[atom]}")
84
+ counter[atom] += 1
85
+ atom_names = [an for i, an in enumerate(atom_names) if i not in del_atoms]
86
+
87
+ coords3d = coords.reshape(-1, 3) / ANG2BOHR
88
+ atoms_xyz = list(zip(atom_names, *coords3d.T))
89
+ rendered = tpl.render(
90
+ atomnum=len(atoms_xyz),
91
+ atoms_xyz=atoms_xyz,
92
+ resno=resno,
93
+ res=res,
94
+ resid=resid,
95
+ segid=segid,
96
+ )
97
+ return rendered
98
+
99
+
100
+ def geom_to_crd_str(geom, **kwargs):
101
+ return atoms_coords_to_crd_str(geom.atoms, geom.cart_coords, **kwargs)
pysisyphus/io/cube.py ADDED
@@ -0,0 +1,220 @@
1
+ from dataclasses import dataclass
2
+ from math import ceil
3
+ import tempfile
4
+ from typing import Tuple
5
+
6
+ import jinja2
7
+ import numpy as np
8
+ from numpy.typing import NDArray
9
+ import pyparsing as pp
10
+
11
+ from pysisyphus.elem_data import ATOMIC_NUMBERS, INV_ATOMIC_NUMBERS
12
+ from pysisyphus.Geometry import Geometry
13
+ from pysisyphus.helpers_pure import file_or_str
14
+ from pysisyphus.wrapper.jmol import view_cdd_cube
15
+
16
+
17
+ def get_grid(coords3d, num=10, offset=3.0):
18
+ minx, miny, minz = coords3d.min(axis=0) - offset
19
+ maxx, maxy, maxz = coords3d.max(axis=0) + offset
20
+ X, Y, Z = np.mgrid[
21
+ minx : maxx : num * 1j,
22
+ miny : maxy : num * 1j,
23
+ minz : maxz : num * 1j,
24
+ ]
25
+ xyz = np.stack((X.flatten(), Y.flatten(), Z.flatten()), axis=1)
26
+ spacing = np.array((maxx - minx, maxy - miny, maxz - minz)) / (num - 1)
27
+ return xyz, spacing, (num, num, num)
28
+
29
+
30
+ def get_grid_with_spacing(coords3d, spacing=0.30, margin=3.0):
31
+ minx, miny, minz = coords3d.min(axis=0) - margin
32
+ maxx, maxy, maxz = coords3d.max(axis=0) + margin
33
+ dx = maxx - minx
34
+ dy = maxy - miny
35
+ dz = maxz - minz
36
+
37
+ nx = ceil(dx / spacing)
38
+ ny = ceil(dy / spacing)
39
+ nz = ceil(dz / spacing)
40
+ X, Y, Z = np.mgrid[
41
+ minx : maxx : nx * 1j,
42
+ miny : maxy : ny * 1j,
43
+ minz : maxz : nz * 1j,
44
+ ]
45
+ xyz = np.stack((X.flatten(), Y.flatten(), Z.flatten()), axis=1)
46
+ act_spacing = np.array(
47
+ ((maxx - minx) / (nx - 1), (maxy - miny) / (ny - 1), (maxz - minz) / (nz - 1))
48
+ )
49
+ return xyz, act_spacing, (nx, ny, nz)
50
+
51
+
52
+ @dataclass
53
+ class Cube:
54
+ atoms: Tuple
55
+ coords3d: NDArray
56
+ origin: NDArray
57
+ npoints: NDArray
58
+ axes: NDArray
59
+ vol_data: NDArray
60
+ comment1: str = "Generated by pysisyphus"
61
+ comment2: str = ""
62
+
63
+ @staticmethod
64
+ def from_file(fn):
65
+ return parse_cube(fn)
66
+
67
+ @staticmethod
68
+ def from_wf_and_grid(wf, vol_data, origin, axes):
69
+ assert vol_data.ndim == 3, "Volume data 'vol_data' must be 3d!"
70
+ return Cube(
71
+ atoms=wf.atoms,
72
+ coords3d=wf.coords3d,
73
+ origin=origin,
74
+ npoints=vol_data.shape,
75
+ axes=axes,
76
+ vol_data=vol_data,
77
+ )
78
+
79
+ def to_str(self):
80
+ return cube_to_str(
81
+ self.atoms,
82
+ self.coords3d,
83
+ self.vol_data.reshape(*self.npoints),
84
+ self.origin,
85
+ self.axes,
86
+ )
87
+
88
+ def write(self, fn):
89
+ with open(fn, "w") as handle:
90
+ handle.write(self.to_str())
91
+
92
+ def view_cdd(self, **kwargs):
93
+ with tempfile.NamedTemporaryFile("w", suffix=".cube") as tmp_cube:
94
+ self.write(tmp_cube.name)
95
+ view_cdd_cube(tmp_cube.name, **kwargs)
96
+
97
+
98
+ @file_or_str(".cube", ".cub")
99
+ def parse_cube(text):
100
+ int_ = pp.common.integer
101
+ sci_real = pp.common.sci_real
102
+
103
+ def get_line_word(*args):
104
+ return pp.Word(*args).setWhitespaceChars("\n")
105
+
106
+ comment = get_line_word(pp.printables + " \t")
107
+ cart_vec = pp.Group(sci_real + sci_real + sci_real)
108
+ axis = pp.Group(int_ + cart_vec)
109
+ atom_line = pp.Group(
110
+ int_.set_results_name("atomic_num")
111
+ + sci_real.set_results_name("charge")
112
+ + cart_vec.set_results_name("coords")
113
+ + pp.LineEnd()
114
+ )
115
+
116
+ parser = (
117
+ comment.set_results_name("comment1")
118
+ + comment.set_results_name("comment2")
119
+ + int_.set_results_name("atom_num")
120
+ + cart_vec.set_results_name("origin")
121
+ + axis.set_results_name("axis1")
122
+ + axis.set_results_name("axis2")
123
+ + axis.set_results_name("axis3")
124
+ + pp.ZeroOrMore(atom_line).set_results_name("atom_lines")
125
+ + pp.ZeroOrMore(sci_real).set_results_name("vol_data")
126
+ )
127
+ res = parser.parseString(text).as_dict()
128
+
129
+ comment1 = " ".join(res["comment1"])
130
+ comment2 = " ".join(res["comment2"])
131
+ atom_num = res["atom_num"]
132
+ atom_lines = res["atom_lines"]
133
+ assert atom_num == len(atom_lines)
134
+
135
+ atom_nums, coords3d = zip(*[(al["atomic_num"], al["coords"]) for al in atom_lines])
136
+ atoms = [INV_ATOMIC_NUMBERS[an].capitalize() for an in atom_nums]
137
+ coords3d = np.array(coords3d)
138
+ origin = np.array(res["origin"])
139
+
140
+ npoints, axes = zip(*[res[k] for k in ("axis1", "axis2", "axis3")])
141
+ npoints = np.array(npoints, int)
142
+ assert all(npoints > 0), "Only Bohr are supported!"
143
+ axes = np.array(axes, float)
144
+ vol_data = np.array(res["vol_data"]).reshape(*npoints)
145
+
146
+ cube = Cube(
147
+ atoms=atoms,
148
+ coords3d=coords3d,
149
+ origin=origin,
150
+ npoints=npoints,
151
+ axes=axes,
152
+ vol_data=vol_data,
153
+ comment1=comment1,
154
+ comment2=comment2,
155
+ )
156
+
157
+ return cube
158
+
159
+
160
+ @file_or_str(".cube", ".cub")
161
+ def geom_from_cube(text, **kwargs):
162
+ cube = parse_cube(text)
163
+ geom = Geometry(cube.atoms, cube.coords3d, **kwargs)
164
+ return geom
165
+
166
+
167
+ CUBE_TPL = """{{ comment1 }}
168
+ {{ comment2 }}
169
+ {{ ifmt(atom_nums|length) }} {{ fmt(origin_x) }} {{ fmt(origin_y) }} {{ fmt(origin_z) }}
170
+ {% for n_points, (x, y, z) in zip(axes_npoints, axes) %}
171
+ {{ ifmt(n_points) }} {{ fmt(x) }} {{ fmt(y) }} {{ fmt(z) }}
172
+ {% endfor %}
173
+ {% for atomic_num, (x, y, z) in zip(atom_nums, coords3d) %}
174
+ {{ ifmt(atomic_num) }} 0.000000 {{ fmt(x) }} {{ fmt(y) }} {{ fmt(z) }}
175
+ {% endfor %}
176
+ {{ grid_str }}
177
+ """
178
+
179
+
180
+ def cube_to_str(atoms, coords3d, vol_data, origin, axes):
181
+ assert vol_data.ndim == 3
182
+ env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
183
+ env.globals.update(zip=zip)
184
+ tpl = env.from_string(CUBE_TPL)
185
+
186
+ org_x, org_y, org_z = origin
187
+ axes_npoints = vol_data.shape
188
+ atom_nums = [ATOMIC_NUMBERS[atom.lower()] for atom in atoms]
189
+ nx, ny, nz = axes_npoints
190
+ grid_str = ""
191
+ for x in range(nx):
192
+ for y in range(ny):
193
+ for z in range(nz):
194
+ d = "{: >12.6e} ".format(vol_data[x, y, z])
195
+ grid_str += d
196
+ if z % 6 == 5:
197
+ grid_str += "\n"
198
+ grid_str += "\n"
199
+
200
+ def fmt(num):
201
+ return f"{num: >12.6f}"
202
+
203
+ def ifmt(num):
204
+ return f"{num: >5d}"
205
+
206
+ rendered = tpl.render(
207
+ fmt=fmt,
208
+ ifmt=ifmt,
209
+ origin_x=org_x,
210
+ origin_y=org_y,
211
+ origin_z=org_z,
212
+ comment1="Generated by pysisyphus",
213
+ comment2=f"Total of {nx*ny*nz} points",
214
+ axes=axes,
215
+ axes_npoints=axes_npoints,
216
+ atom_nums=atom_nums,
217
+ coords3d=coords3d,
218
+ grid_str=grid_str,
219
+ )
220
+ return rendered
pysisyphus/io/fchk.py ADDED
@@ -0,0 +1,184 @@
1
+ import re
2
+
3
+ import numpy as np
4
+
5
+ from pysisyphus.elem_data import INV_ATOMIC_NUMBERS
6
+ from pysisyphus.Geometry import Geometry
7
+ from pysisyphus.helpers_pure import file_or_str
8
+ from pysisyphus.wavefunction.shells import Shell, FCHKShells
9
+ from pysisyphus.wavefunction import Wavefunction
10
+ from pysisyphus.wavefunction.helpers import BFType
11
+
12
+
13
+ @file_or_str(".fchk")
14
+ def parse_fchk(text):
15
+ header_re = r"\s+".join(
16
+ (
17
+ r"(?P<keyword>[\w\-\s/\(\)=\*]+)",
18
+ r"(?P<kind>[HIRCL])",
19
+ r"(?P<length>N=)?",
20
+ r"(?P<num>[\d\-\+\.E]+)",
21
+ )
22
+ )
23
+ header_re = re.compile(header_re)
24
+
25
+ cur_keyword = None
26
+ lines = text.strip().split("\n")
27
+ # title1, titl2 are not used
28
+ _, _, *lines = lines
29
+
30
+ conv_funcs = {
31
+ "R": lambda line: [float(_) for _ in line.split()], # real
32
+ "I": lambda line: [int(_) for _ in line.split()], # integer
33
+ "C": lambda line: [line], # character
34
+ "H": lambda line: [bool(_) for _ in line.split()], # logical
35
+ "L": lambda line: [bool(_) for _ in line.split()], # logical
36
+ }
37
+
38
+ data = {}
39
+ for line in lines:
40
+ line = line.strip()
41
+ if mobj := header_re.match(line):
42
+ cur_keyword = mobj.group("keyword").strip()
43
+ conv_func = conv_funcs[mobj.group("kind")]
44
+ if mobj.group("length"):
45
+ data[cur_keyword] = list()
46
+ else:
47
+ data[cur_keyword] = conv_func(mobj.group("num"))[0]
48
+ continue
49
+ data[cur_keyword].extend(conv_func(line))
50
+ return data
51
+
52
+
53
+ @file_or_str(".fchk")
54
+ def shells_from_fchk(text):
55
+ data = parse_fchk(text)
56
+
57
+ def to_arr(key, dtype):
58
+ return np.array(data[key], dtype=dtype)
59
+
60
+ """
61
+ These coordinates may not coincide with the basis functions?! Nonetheless,
62
+ basis functions are usually atom-centered. But just to be sure we use the
63
+ entries from 'Coordinates of each shell'.
64
+ # coords3d = to_arr("Current cartesian coordinates", float).reshape(-1, 3)
65
+ """
66
+ atomic_nums = to_arr("Atomic numbers", int)
67
+
68
+ centers = to_arr("Shell to atom map", int) - 1
69
+ ang_moms = to_arr("Shell types", int)
70
+ prims_per_shell = to_arr("Number of primitives per shell", int)
71
+
72
+ prim_exponents = to_arr("Primitive exponents", float)
73
+ prim_coeffs = to_arr("Contraction coefficients", float)
74
+ try:
75
+ p_prim_coeffs = to_arr("P(S=P) Contraction coefficients", float)
76
+ except KeyError:
77
+ p_prim_coeffs = np.zeros_like(prim_coeffs)
78
+ shell_coords3d = to_arr("Coordinates of each shell", float).reshape(-1, 3)
79
+
80
+ _shells = list()
81
+ prim_ind = 0
82
+ for i, (center_ind, L, prim_num) in enumerate(
83
+ zip(centers, ang_moms, prims_per_shell)
84
+ ):
85
+ # 0: s, 1: p, -1: sp, 2: 6d, -2: 5d, 3: 10f, -3: 7f, etc...
86
+ assert L <= 1, "Only spherical basis functions are supported."
87
+ # Convert SP-shell with L = -1 to a s-shell with L = 0.
88
+ # The p-shell is also created later.
89
+ L, is_sp = (0, True) if (L == -1) else (L, False)
90
+
91
+ coeffs = list()
92
+ p_coeffs = list()
93
+ exps = list()
94
+ for _ in range(prim_num):
95
+ coeffs.append(prim_coeffs[prim_ind])
96
+ p_coeffs.append(p_prim_coeffs[prim_ind])
97
+ exps.append(prim_exponents[prim_ind])
98
+ prim_ind += 1
99
+ atomic_num = atomic_nums[center_ind]
100
+ center = shell_coords3d[i]
101
+
102
+ def append_shell(coeffs, L=L):
103
+ shell = Shell(
104
+ L=abs(L),
105
+ atomic_num=atomic_num,
106
+ center=center,
107
+ coeffs=coeffs,
108
+ exps=exps,
109
+ center_ind=center_ind,
110
+ )
111
+ _shells.append(shell)
112
+
113
+ # Create shell with actual L.
114
+ # If we deal with a SP shell we also create the P shell below.
115
+ append_shell(coeffs)
116
+ if is_sp:
117
+ # Explicitly create p-shell
118
+ append_shell(p_coeffs, L=1)
119
+ shells = FCHKShells(_shells)
120
+ return shells
121
+
122
+
123
+ def atoms_from_data(data):
124
+ atomic_numbers = data["Atomic numbers"]
125
+ atoms = tuple([INV_ATOMIC_NUMBERS[Z] for Z in atomic_numbers])
126
+ return atoms
127
+
128
+
129
+ @file_or_str(".fchk")
130
+ def wavefunction_from_fchk(text):
131
+ data = parse_fchk(text)
132
+
133
+ charge = data["Charge"]
134
+ mult = data["Multiplicity"]
135
+ atoms = atoms_from_data(data)
136
+ coords = data["Current cartesian coordinates"]
137
+ unrestricted = "Beta Orbital Energies" in data
138
+
139
+ def mos_for_spin(spin):
140
+ mo_ens = data[f"{spin} Orbital Energies"]
141
+ mo_num = len(mo_ens)
142
+ mo_coeffs = np.array(data[f"{spin} MO coefficients"])
143
+ mo_coeffs = mo_coeffs.reshape(mo_num, mo_num)
144
+ return mo_coeffs
145
+
146
+ C_a = mos_for_spin("Alpha").T
147
+ occ_a = data["Number of alpha electrons"]
148
+ if unrestricted:
149
+ C_b = mos_for_spin("Beta")
150
+ occ_b = data["Number of beta electrons"]
151
+ else:
152
+ C_b = C_a.copy()
153
+ occ_b = occ_a
154
+ C = np.stack((C_a, C_b))
155
+ shells = shells_from_fchk(text)
156
+ return Wavefunction(
157
+ atoms=atoms,
158
+ coords=coords,
159
+ charge=charge,
160
+ mult=mult,
161
+ unrestricted=unrestricted,
162
+ occ=(occ_a, occ_b),
163
+ C=C,
164
+ bf_type=BFType.PURE_SPHERICAL,
165
+ shells=shells,
166
+ )
167
+
168
+
169
+ @file_or_str(".fchk")
170
+ def geom_from_fchk(text, **geom_kwargs):
171
+ data = parse_fchk(text)
172
+ atoms = atoms_from_data(data)
173
+
174
+ try:
175
+ coords = data["Opt point 1 Geometries"]
176
+ except KeyError:
177
+ coords = data["Current cartesian coordinates"]
178
+ coords = np.array(coords)
179
+ coords = coords.reshape(-1, 3 * len(atoms))
180
+
181
+ geoms = [Geometry(atoms, coords_, **geom_kwargs) for coords_ in coords]
182
+ if len(geoms) == 1:
183
+ geoms = geoms[0]
184
+ return geoms
pysisyphus/io/hdf5.py ADDED
@@ -0,0 +1,49 @@
1
+ import h5py
2
+
3
+
4
+ def init_h5_group(f, group_name, data_model):
5
+ """Create group with given name and data model."""
6
+ group = f.create_group(group_name)
7
+ # Create (resizable) datasets by using None in maxshape
8
+ for key, shape in data_model.items():
9
+ maxshape = (None,) if (len(shape) == 1) else (None, *shape[1:])
10
+ group.create_dataset(key, shape, maxshape=maxshape)
11
+
12
+
13
+ def get_h5_group(fn, group_name, data_model=None, reset=False):
14
+ """Return (and create if neccesary) group with given name and
15
+ data model."""
16
+
17
+ f = h5py.File(fn, mode="a")
18
+ # Shortcut
19
+ if data_model is None:
20
+ return f[group_name]
21
+
22
+ if group_name not in f:
23
+ init_h5_group(f, group_name, data_model)
24
+ group = f[group_name]
25
+
26
+ # Check compatibility of data_model and group. If they aren't compatible
27
+ # recreate the group with the proper shapes.
28
+ try:
29
+ compatible = [group[key].shape == shape for key, shape in data_model.items()]
30
+ except KeyError:
31
+ compatible = (False,)
32
+ compatible = all(compatible)
33
+ if (not compatible) or reset:
34
+ del f[group_name]
35
+ init_h5_group(f, group_name, data_model)
36
+ group = f[group_name]
37
+
38
+ return group
39
+
40
+
41
+ def resize_h5_group(group, max_cycles):
42
+ """Increase size of first dimension of datasets in the given group."""
43
+ for key, dataset in group.items():
44
+ # No need to resize scalar datasets
45
+ if dataset.shape == ():
46
+ continue
47
+ new_shape = list(dataset.shape).copy()
48
+ new_shape[0] = max_cycles
49
+ dataset.resize(new_shape)
@@ -0,0 +1,72 @@
1
+ import numpy as np
2
+ import h5py
3
+
4
+ from pysisyphus.Geometry import Geometry
5
+ from pysisyphus.helpers_pure import eigval_to_wavenumber
6
+
7
+
8
+ def save_hessian(h5_fn, geom, cart_hessian=None, energy=None, mult=None):
9
+ if cart_hessian is None:
10
+ cart_hessian = geom.cart_hessian
11
+
12
+ if energy is None:
13
+ energy = geom.energy
14
+
15
+ if mult is None:
16
+ mult = geom.calculator.mult
17
+
18
+ if len(geom.atoms) > 1:
19
+ proj_hessian = geom.eckart_projection(geom.mass_weigh_hessian(cart_hessian))
20
+ else:
21
+ proj_hessian = cart_hessian
22
+ eigvals, _ = np.linalg.eigh(proj_hessian)
23
+ vibfreqs = eigval_to_wavenumber(eigvals)
24
+
25
+ masses = geom.masses
26
+ atoms = geom.atoms
27
+ coords3d = geom.coords3d
28
+
29
+ # with h5py.File(h5_fn, "w") as handle:
30
+ # handle.create_dataset("hessian", data=cart_hessian)
31
+ # handle.create_dataset("vibfreqs", data=vibfreqs)
32
+ # handle.create_dataset("masses", data=masses)
33
+ # handle.create_dataset("coords3d", data=coords3d)
34
+
35
+ # try:
36
+ # handle.attrs["atoms"] = [atom.lower() for atom in atoms]
37
+ # except OSError:
38
+ # handle.create_dataset("atoms", data=[atom.lower() for atom in atoms])
39
+ # handle.attrs["energy"] = energy
40
+ # handle.attrs["mult"] = mult
41
+
42
+
43
+ def save_third_deriv(h5_fn, geom, third_deriv_result, H_mw, H_proj):
44
+ # with h5py.File(h5_fn, "w") as handle:
45
+ # for key, value in third_deriv_result._asdict().items():
46
+ # handle.create_dataset(key, data=value)
47
+
48
+ # handle.create_dataset("coords3d", data=geom.coords3d)
49
+ # handle.create_dataset("masses", data=geom.masses)
50
+ # handle.create_dataset("H_mw", data=H_mw)
51
+ # handle.create_dataset("H_proj", data=H_proj)
52
+ # try:
53
+ # handle.attrs["atoms"] = [atom.lower() for atom in atoms]
54
+ # except OSError:
55
+ # handle.create_dataset("atoms", data=[atom.lower() for atom in atoms])
56
+ pass
57
+
58
+
59
+ def geom_from_hessian(h5_fn, **geom_kwargs):
60
+ with h5py.File(h5_fn, "r") as handle:
61
+ try:
62
+ atoms = [atom.capitalize() for atom in handle.attrs["atoms"]]
63
+ except (KeyError, TypeError):
64
+ atoms = [atom.capitalize() for atom in handle["atoms"][:]]
65
+ coords3d = handle["coords3d"][:]
66
+ energy = handle.attrs["energy"]
67
+ cart_hessian = handle["hessian"][:]
68
+
69
+ geom = Geometry(atoms=atoms, coords=coords3d, **geom_kwargs)
70
+ geom.cart_hessian = cart_hessian
71
+ geom.energy = energy
72
+ return geom