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,1171 @@
1
+ # [1] https://aip.scitation.org/doi/pdf/10.1063/5.0004046
2
+ # Efficient implementation of the superposition of atomic potentials initial
3
+ # guess for electronic structure calculations in Gaussian basis sets
4
+ # Lehtola, Visscher, Engel, 2020
5
+
6
+ import itertools as it
7
+ from math import sqrt, log, pi
8
+ from pathlib import Path
9
+ import textwrap
10
+ from typing import List, Literal
11
+
12
+
13
+ import h5py
14
+ from jinja2 import Template
15
+ from joblib import Memory
16
+ import numpy as np
17
+ from numpy.typing import NDArray
18
+ import scipy as sp
19
+
20
+
21
+ from pysisyphus.config import L_MAX, L_AUX_MAX
22
+ from pysisyphus.elem_data import (
23
+ ATOMIC_NUMBERS,
24
+ INV_ATOMIC_NUMBERS,
25
+ nuc_charges_for_atoms,
26
+ )
27
+ from pysisyphus.helpers_pure import file_or_str
28
+ from pysisyphus.linalg import multi_component_sym_mat
29
+ from pysisyphus.wavefunction.helpers import (
30
+ canonical_order,
31
+ get_l,
32
+ get_shell_shape,
33
+ L_MAP_INV,
34
+ permut_for_order,
35
+ )
36
+
37
+ from pysisyphus.wavefunction.ints import (
38
+ cart_gto3d,
39
+ coulomb3d,
40
+ diag_quadrupole3d,
41
+ dipole3d,
42
+ kinetic3d,
43
+ ovlp3d,
44
+ quadrupole3d,
45
+ int2c2e3d,
46
+ int3c2e3d_sph,
47
+ )
48
+
49
+ from pysisyphus.wavefunction.cart2sph import cart2sph_coeffs
50
+ from pysisyphus.wavefunction.normalization import norm_cgto_lmn
51
+
52
+
53
+ class Shell:
54
+ def __init__(
55
+ self,
56
+ L: int,
57
+ center: NDArray[float],
58
+ coeffs: NDArray[float],
59
+ exps: NDArray[float],
60
+ center_ind: int,
61
+ atomic_num=None,
62
+ shell_index=None,
63
+ index=None,
64
+ # min_coeff: float = 1e-8,
65
+ ):
66
+ self.L = get_l(L)
67
+ self.center = np.array(center, dtype=float) # (x, y, z), 1d array
68
+ coeffs = np.array(coeffs, dtype=float)
69
+ # cur_min_coeff = np.abs(coeffs).min()
70
+ # assert (
71
+ # cur_min_coeff >= min_coeff
72
+ # ), f"Got invalid contraction coeff of '{cur_min_coeff:.6f}'"
73
+ # Store original contraction coefficients
74
+ self.coeffs_org = coeffs.copy()
75
+ # Orbital exponents, 1d array
76
+ self.exps = np.array(exps, dtype=float)
77
+ assert self.coeffs_org.size == self.exps.size
78
+ coeffs = np.array(coeffs)
79
+ assert coeffs.size == self.exps.size
80
+ self.center_ind = int(center_ind)
81
+ if atomic_num is None:
82
+ atomic_num = -1
83
+ self.atomic_num = int(atomic_num)
84
+ if shell_index is not None:
85
+ self.shell_index = shell_index
86
+ if index is not None:
87
+ self.index = index
88
+
89
+ self.coeffs, _ = norm_cgto_lmn(coeffs, self.exps, self.L)
90
+
91
+ try:
92
+ self.atom = INV_ATOMIC_NUMBERS[self.atomic_num]
93
+ except KeyError:
94
+ self.atom = "X"
95
+ self.atom = self.atom.lower()
96
+
97
+ def as_tuple(self):
98
+ return self.L, self.center, self.coeffs, self.exps, self.index, self.size
99
+
100
+ def exps_coeffs_iter(self):
101
+ return zip(self.exps, self.coeffs)
102
+
103
+ @property
104
+ def contr_depth(self):
105
+ return self.coeffs.size
106
+
107
+ @property
108
+ def cart_powers(self):
109
+ return np.array(canonical_order(self.L), dtype=int)
110
+
111
+ @property
112
+ def size(self):
113
+ """Number of cartesian basis functions in the shell."""
114
+ return self.cart_size
115
+
116
+ @property
117
+ def cart_size(self):
118
+ """Number of cartesian basis functions in the shell."""
119
+ L = self.L
120
+ return (L + 1) * (L + 2) // 2
121
+
122
+ @property
123
+ def sph_size(self):
124
+ """Number of cartesian basis functions in the shell."""
125
+ return 2 * self.L + 1
126
+
127
+ @property
128
+ def index(self) -> int:
129
+ return self._index
130
+
131
+ @index.setter
132
+ def index(self, index: int):
133
+ self._index = int(index)
134
+
135
+ @property
136
+ def shell_index(self) -> int:
137
+ return self._shell_index
138
+
139
+ @shell_index.setter
140
+ def shell_index(self, shell_index: int):
141
+ self._shell_index = shell_index
142
+
143
+ def to_pyscf_shell(self):
144
+ key = L_MAP_INV[self.L]
145
+ lines = [
146
+ f"{self.atom.title()} {key.upper()}",
147
+ ]
148
+ lines += [
149
+ f"{exp_:> 18.10f} {coeff:>18.10f}"
150
+ for exp_, coeff in zip(self.exps, self.coeffs)
151
+ ]
152
+ return "\n".join(lines)
153
+
154
+ def to_sap_shell(self):
155
+ """Fix contraction coefficients, for use in SAP-initial guess calculation.
156
+
157
+ See [1]."""
158
+ # SAP shells must always be s-shells
159
+ assert self.L == 0
160
+ self.coeffs = self.coeffs_org
161
+ coeffs_sum = self.coeffs.sum()
162
+ # Check sum rule; sum should equal -Z.
163
+ assert (coeffs_sum + ATOMIC_NUMBERS[self.atom.lower()]) <= 1e-5
164
+ # The potential fits were carried out for functions of the form:
165
+ # g_p(r) = (α_p / π)**1.5 * exp(-α_p / r) .
166
+ # So we multiply the prefactor onto the contraction coefficients.
167
+ # See [1], p. 152, just above Eq. (2).
168
+ self.coeffs *= (self.exps / np.pi) ** 1.5
169
+
170
+ def __str__(self):
171
+ try:
172
+ center_str = f", at atom {self.center_ind}"
173
+ except AttributeError:
174
+ center_str = ""
175
+ return f"Shell(L={self.L}, {self.contr_depth} pGTO{center_str})"
176
+
177
+ def __repr__(self):
178
+ return self.__str__()
179
+
180
+
181
+ Ordering = Literal["native", "pysis"]
182
+
183
+
184
+ class Shells:
185
+ sph_Ps = {l: np.eye(2 * l + 1) for l in range(max(L_MAX, L_AUX_MAX) + 1)}
186
+
187
+ def __init__(
188
+ self,
189
+ shells: List[Shell],
190
+ screen: bool = False,
191
+ cache: bool = False,
192
+ cache_path: str = "./cache",
193
+ ordering: Ordering = "native",
194
+ ):
195
+ self.shells = shells
196
+ self.ordering = ordering
197
+ self.screen = screen
198
+ self.cache = cache
199
+ self.cache_path = Path(cache_path)
200
+ """
201
+ 'native' refers to the original ordering, as used in the QC program. The
202
+ ordering will be reflected in the MO coefficient ordering. With 'native'
203
+ the integrals calculated by pysisyphus must be reorderd, to match the native
204
+ ordering of the MO coefficients.
205
+ """
206
+ assert ordering in ("pysis", "native")
207
+
208
+ # Now that we have all Shell objects, we can set their starting indices
209
+ index = 0
210
+ shell_index = 0
211
+ for shell in self.shells:
212
+ shell.shell_index = shell_index
213
+ shell.index = index
214
+ index += shell.size
215
+ shell_index += 1
216
+
217
+ # Try to construct Cartesian permutation matrix from cart_order, if defnied.
218
+ try:
219
+ self.cart_Ps = permut_for_order(self.cart_order)
220
+ except AttributeError:
221
+ pass
222
+
223
+ self.atoms, self.coords3d = self.atoms_coords3d
224
+ # Precontract & store coefficients for reordering spherical basis functions
225
+ # and converting them from Cartesian basis functions.
226
+ self.reorder_c2s_coeffs = self.P_sph @ self.cart2sph_coeffs
227
+
228
+ # Enable disk cache for 1el-integrals
229
+ if self.cache:
230
+ self.memory = Memory(self.cache_path, verbose=0)
231
+ self.get_1el_ints_cart = self.memory.cache(self.get_1el_ints_cart)
232
+ self.get_1el_ints_sph = self.memory.cache(self.get_1el_ints_sph)
233
+
234
+ def __len__(self):
235
+ return len(self.shells)
236
+
237
+ def __getitem__(self, key):
238
+ return self.shells[key]
239
+
240
+ def print_shells(self):
241
+ for shell in self.shells:
242
+ print(shell)
243
+
244
+ def as_tuple(self):
245
+ # Ls, centers, contr_coeffs, exponents, indices, sizes
246
+ return zip(*[shell.as_tuple() for shell in self.shells])
247
+
248
+ @property
249
+ def cart_size(self):
250
+ """Number of cartesian basis functions."""
251
+ return sum([shell.cart_size for shell in self.shells])
252
+
253
+ @property
254
+ def sph_size(self):
255
+ """Number of spherical basis."""
256
+ return sum([shell.sph_size for shell in self.shells])
257
+
258
+ def as_arrays(self, fortran=False):
259
+ """Similar to the approach in libcint."""
260
+ # bas_centers
261
+ # One entry per shell, integer array.
262
+ # center_ind, atomic number, pointer to center coordinates in bas_data (3 integers)
263
+ bas_centers = list()
264
+ # bas_spec
265
+ # One entry per shell, integer array.
266
+ # shell_ind, total angmom, N_pgto, N_cgto, \
267
+ # pointer to contraction coefficients and exponents in bas_data \
268
+ # (2*N_pgto floats)
269
+ bas_spec = list()
270
+ # bas_data
271
+ # Float array. Starts with 3 * N_centers floats, containing the center
272
+ # coordinates. Continues with alternating contraction coefficient and
273
+ # orbital exponent data.
274
+ bas_data = list()
275
+ pointer = 1 if fortran else 0
276
+
277
+ shells = self.shells
278
+ # Store center data, i.e., where the basis functions are located.
279
+ for shell in shells:
280
+ center_ind = shell.center_ind
281
+ bas_data.extend(shell.center) # Store coordinates
282
+ atomic_num = shell.atomic_num
283
+ if atomic_num is None:
284
+ atomic_num = -1
285
+ bas_centers.append((center_ind, atomic_num, pointer))
286
+ pointer += 3
287
+
288
+ # Store contraction coefficients and primitive exponents.
289
+ for shell_ind, shell in enumerate(self.shells):
290
+ # L, center, coeffs, exponents, index, size = shell.as_tuple()
291
+ L, _, _, exponents, _, _ = shell.as_tuple()
292
+ contr_coeffs = shell.coeffs_org
293
+ nprim = len(contr_coeffs)
294
+ # ncontr is hardcoded to 1 for now, as there are no special functions
295
+ # to handle general contractions.
296
+ ncontr = 1
297
+ bas_spec.append((shell_ind, L, nprim, ncontr, pointer))
298
+ bas_data.extend(contr_coeffs)
299
+ pointer += nprim
300
+ bas_data.extend(exponents)
301
+ pointer += nprim
302
+
303
+ bas_centers = np.array(bas_centers, dtype=int)
304
+ bas_spec = np.array(bas_spec, dtype=int)
305
+ bas_data = np.array(bas_data, dtype=float)
306
+ return bas_centers, bas_spec, bas_data
307
+
308
+ def to_sympleints_arrays(self):
309
+ shells = self.shells
310
+ # center_ind, atomic_num, L, nprims
311
+ centers = np.zeros((len(shells), 3))
312
+ shell_data = list()
313
+ coefficients = list()
314
+ exponents = list()
315
+ for i, shell in enumerate(shells):
316
+ coeffs = shell.coeffs_org
317
+ coefficients.extend(coeffs.tolist())
318
+ exponents.extend(shell.exps.tolist())
319
+ nprims = len(coeffs)
320
+
321
+ centers[i] = shell.center
322
+ atomic_num = shell.atomic_num
323
+ if atomic_num is None:
324
+ atomic_num = -1
325
+ shell_data.append((shell.center_ind, atomic_num, shell.L, nprims))
326
+ shell_data = np.array(shell_data, dtype=np.int32)
327
+ coefficients = np.array(coefficients)
328
+ exponents = np.array(exponents)
329
+ return shell_data, centers, coefficients, exponents
330
+
331
+ def dump_to_h5_group(self, h5_handle, group_name):
332
+ group = h5_handle.create_group(group_name)
333
+ shell_data, centers, coefficients, exponents = self.to_sympleints_arrays()
334
+ group.create_dataset("shell_data", data=shell_data)
335
+ group.create_dataset("centers", data=centers)
336
+ group.create_dataset("coefficients", data=coefficients)
337
+ group.create_dataset("exponents", data=exponents)
338
+
339
+ @property
340
+ def l_max(self):
341
+ return max([shell.L for shell in self.shells])
342
+
343
+ @property
344
+ def atoms_coords3d(self):
345
+ atoms = list()
346
+ coords3d = list()
347
+ center_inds = list()
348
+ for shell in self.shells:
349
+ center_ind = shell.center_ind
350
+ # Skip cycle if we already registered this center
351
+ if center_ind in center_inds:
352
+ continue
353
+ else:
354
+ center_inds.append(center_ind)
355
+ try:
356
+ atom = INV_ATOMIC_NUMBERS[shell.atomic_num]
357
+ # Use dummy atom when atomic_num is not set / None
358
+ except KeyError:
359
+ atom = "X"
360
+ atoms.append(atom)
361
+ center = shell.center
362
+ coords3d.append(center)
363
+ coords3d = np.array(coords3d)
364
+ return atoms, coords3d
365
+
366
+ @property
367
+ def cart_bf_num(self):
368
+ return sum([shell.size for shell in self.shells])
369
+
370
+ def from_basis(self, name, shells_cls=None, **kwargs):
371
+ from pysisyphus.wavefunction.Basis import shells_with_basis
372
+
373
+ atoms, coords3d = self.atoms_coords3d
374
+ if shells_cls is None:
375
+ shells_cls = type(self)
376
+ return shells_with_basis(
377
+ atoms, coords3d, name=name, shells_cls=shells_cls, **kwargs
378
+ )
379
+
380
+ @staticmethod
381
+ @file_or_str(".in")
382
+ def from_aomix(text):
383
+ # import here, to avoid cyclic imports
384
+ from pysisyphus.io.aomix import shells_from_aomix
385
+
386
+ shells = shells_from_aomix(text)
387
+ return shells
388
+
389
+ @staticmethod
390
+ @file_or_str(".json")
391
+ def from_orca_json(text):
392
+ # import here, to avoid cyclic imports
393
+ from pysisyphus.io.orca import shells_from_json
394
+
395
+ shells = shells_from_json(text)
396
+ return shells
397
+
398
+ @staticmethod
399
+ @file_or_str(".fchk")
400
+ def from_fchk(text):
401
+ # import here, to avoid cyclic imports
402
+ from pysisyphus.io.fchk import shells_from_fchk
403
+
404
+ shells = shells_from_fchk(text)
405
+ return shells
406
+
407
+ @staticmethod
408
+ def from_sympleints_arrays(shell_data, centers, coefficients, exponents, **kwargs):
409
+ shells = list()
410
+ pointer = 0
411
+ for i, (center_ind, atomic_num, L, nprims) in enumerate(shell_data):
412
+ coeffs = coefficients[pointer : pointer + nprims]
413
+ exps = exponents[pointer : pointer + nprims]
414
+ shell = Shell(L, centers[i], coeffs, exps, center_ind, atomic_num)
415
+ pointer += nprims
416
+ shells.append(shell)
417
+ return Shells(shells, **kwargs)
418
+
419
+ @staticmethod
420
+ def from_sympleints_h5(h5_fn, group_name="shells"):
421
+ with h5py.File(h5_fn, "r") as handle:
422
+ group = handle[group_name]
423
+ shell_data = group["shell_data"][:]
424
+ centers = group["centers"][:]
425
+ coefficients = group["coefficients"][:]
426
+ exponents = group["exponents"][:]
427
+ return Shells.from_sympleints_arrays(
428
+ shell_data, centers, coefficients, exponents
429
+ )
430
+
431
+ @staticmethod
432
+ def from_pyscf_mol(mol):
433
+ shells = list()
434
+ for bas_id in range(mol.nbas):
435
+ L = mol.bas_angular(bas_id)
436
+ center = mol.bas_coord(bas_id)
437
+ coeffs = mol.bas_ctr_coeff(bas_id).flatten()
438
+ exps = mol.bas_exp(bas_id)
439
+ assert (
440
+ coeffs.size == exps.size
441
+ ), "General contractions are not yet supported."
442
+ center_ind = mol.bas_atom(bas_id)
443
+ atom_symbol = mol.atom_symbol(center_ind)
444
+ atomic_num = ATOMIC_NUMBERS[atom_symbol.lower()]
445
+ shell = Shell(L, center, coeffs, exps, center_ind, atomic_num)
446
+ shells.append(shell)
447
+ return PySCFShells(shells)
448
+
449
+ def to_pyscf_mol(self):
450
+ # TODO: this would be better suited as a method of Wavefunction,
451
+ # as pyscf Moles must have sensible spin & charge etc.
452
+ # TODO: currently, this does not support different basis sets
453
+ # for the same element.
454
+ try:
455
+ from pyscf import gto
456
+ except ModuleNotFoundError:
457
+ return None
458
+
459
+ unique_atoms = set()
460
+ basis = dict()
461
+ atoms_coords = list()
462
+ for _, center_shells in self.center_shell_iter():
463
+ center_shells = list(center_shells)
464
+ shell0 = center_shells[0]
465
+ atom = shell0.atom
466
+ x, y, z = shell0.center
467
+ atoms_coords.append(f"{atom} {x} {y} {z}")
468
+ if atom not in unique_atoms:
469
+ unique_atoms.add(atom)
470
+ else:
471
+ continue
472
+ atom_shells = "\n".join([shell.to_pyscf_shell() for shell in center_shells])
473
+ basis[atom] = gto.basis.parse(atom_shells)
474
+
475
+ mol = gto.Mole()
476
+ mol.atom = "; ".join(atoms_coords)
477
+ mol.unit = "Bohr"
478
+ mol.basis = basis
479
+ mol.build()
480
+ return mol
481
+
482
+ def center_shell_iter(self):
483
+ sorted_shells = sorted(self.shells, key=lambda shell: shell.center_ind)
484
+ return it.groupby(sorted_shells, key=lambda shell: shell.center_ind)
485
+
486
+ def fragment_ao_map(self, fragments):
487
+ frag_map = dict()
488
+ for i, frag in enumerate(fragments):
489
+ for center_ind in frag:
490
+ frag_map[center_ind] = i
491
+
492
+ frag_ao_map = dict()
493
+ for i, ao_center in enumerate(self.sph_ao_centers):
494
+ frag_ind = frag_map[ao_center]
495
+ frag_ao_map.setdefault(frag_ind, list()).append(i)
496
+ return frag_ao_map
497
+
498
+ def as_gau_gbs(self) -> str:
499
+ def dfmt(num):
500
+ return f"{num: 12.10e}".replace("e", "D")
501
+
502
+ gbs_tpl = Template(
503
+ """
504
+ {% for center_ind, shells in center_shell_iter %}
505
+ {{ center_ind+1 }} 0
506
+ {%- for shell in shells %}
507
+ {{ ("S", "P", "D", "F", "G")[shell.L] }} {{ shell.exps.size}} 1.00 0.000000000000
508
+ {%- for exp_, coeff in shell.exps_coeffs_iter() %}
509
+ {{ dfmt(exp_) }} {{ dfmt(coeff) }}
510
+ {%- endfor -%}
511
+ {% endfor %}
512
+ ****
513
+ {%- endfor %}
514
+ """
515
+ )
516
+ rendered = gbs_tpl.render(center_shell_iter=self.center_shell_iter(), dfmt=dfmt)
517
+ rendered = textwrap.dedent(rendered)
518
+ return rendered
519
+
520
+ def _ao_center_iter(self, func):
521
+ i = 0
522
+ for shell in self.shells:
523
+ for _ in func(shell.L):
524
+ yield shell.center_ind
525
+ i += 0
526
+
527
+ @property
528
+ def cart_ao_centers(self):
529
+ return self._ao_center_iter(canonical_order)
530
+
531
+ @property
532
+ def sph_ao_centers(self):
533
+ return self._ao_center_iter(lambda l: range(2 * l + 1))
534
+
535
+ @property
536
+ def cart2sph_coeffs(self):
537
+ cart2sph = cart2sph_coeffs(self.l_max)
538
+ C = sp.linalg.block_diag(*[cart2sph[shell.L] for shell in self.shells])
539
+ return C
540
+
541
+ @property
542
+ def P_sph(self):
543
+ """Permutation matrix for spherical basis functions."""
544
+ return sp.linalg.block_diag(*[self.sph_Ps[shell.L] for shell in self.shells])
545
+
546
+ @property
547
+ def P_cart(self):
548
+ """Permutation matrix for Cartesian basis functions."""
549
+ try:
550
+ P_cart = sp.linalg.block_diag(
551
+ *[self.cart_Ps[shell.L] for shell in self.shells]
552
+ )
553
+ except AttributeError:
554
+ P_cart = np.eye(self.cart_size)
555
+ return P_cart
556
+
557
+ def eval(self, xyz, spherical=True):
558
+ """Evaluate all basis functions at points xyz using generated code.
559
+
560
+ A possibly more efficient approach is discussed in III C of
561
+ https://doi.org/10.1063/1.469408.
562
+ """
563
+ if spherical:
564
+ precontr = self.cart2sph_coeffs.T @ self.P_sph.T
565
+ else:
566
+ precontr = self.P_cart.T
567
+ ncbfs = precontr.shape[0]
568
+ npoints = len(xyz)
569
+
570
+ coords3d = self.coords3d
571
+ grid_vals = np.zeros((npoints, ncbfs))
572
+ for center_ind, shells in self.center_shell_iter():
573
+ # Create list from the shells iterator, otherwise it will be empty
574
+ # after the first pass over all points.
575
+ shells = list(shells)
576
+ for i, R in enumerate(xyz):
577
+ for shell in shells:
578
+ La, A, da, ax, a_ind, a_size = shell.as_tuple()
579
+ grid_vals[i, a_ind : a_ind + a_size] = cart_gto3d.cart_gto3d[(La,)](
580
+ ax, da, A, R
581
+ )
582
+ return grid_vals @ precontr
583
+
584
+ def get_1el_ints_cart(
585
+ self,
586
+ func_dict,
587
+ other=None,
588
+ can_reorder=True,
589
+ components=0,
590
+ screen_func=None,
591
+ **kwargs,
592
+ ):
593
+ shells_a = self
594
+ symmetric = other is None
595
+ shells_b = shells_a if symmetric else other
596
+ cart_bf_num_a = shells_a.cart_bf_num
597
+ cart_bf_num_b = shells_b.cart_bf_num
598
+
599
+ # components = 0 indicates, that a plain 2d matrix is desired.
600
+ if is_2d := (components == 0):
601
+ components = 1
602
+
603
+ # Preallocate empty matrices and directly assign the calculated values
604
+ integrals = np.zeros((components, cart_bf_num_a, cart_bf_num_b))
605
+
606
+ # Dummy screen function that always returns True
607
+ if (not self.screen) or (screen_func is None):
608
+ screen_func = lambda *args: True
609
+
610
+ for i, shell_a in enumerate(shells_a):
611
+ La, A, da, ax, a_ind, a_size = shell_a.as_tuple()
612
+ a_slice = slice(a_ind, a_ind + a_size)
613
+ # When we don't deal with symmetric matrices we have to iterate over
614
+ # all other basis functions in shells_b, so we reset i to 0.
615
+ if not symmetric:
616
+ i = 0
617
+ a_min_exp = ax.min() # Minimum exponent used for screening
618
+ for shell_b in shells_b[i:]:
619
+ Lb, B, db, bx, b_ind, b_size = shell_b.as_tuple()
620
+ b_slice = slice(b_ind, b_ind + b_size)
621
+ # Screen shell pair
622
+ b_min_exp = bx.min() # Minimum exponent used for screening
623
+ R_ab = np.linalg.norm(A - B) # Shell center distance
624
+ b_size = get_shell_shape(Lb)[0]
625
+ if not screen_func(a_min_exp, b_min_exp, R_ab):
626
+ continue
627
+ integrals[:, a_slice, b_slice] = func_dict[(La, Lb)](
628
+ ax[:, None],
629
+ da[:, None],
630
+ A,
631
+ bx[None, :],
632
+ db[None, :],
633
+ B,
634
+ **kwargs,
635
+ )
636
+ # Fill up the lower tringular part of the matrix in the symmetric
637
+ # case.
638
+ # TODO: this may be (?) better done outside of this loop, after
639
+ # everything is calculated...
640
+ if symmetric:
641
+ integrals[:, b_slice, a_slice] = np.transpose(
642
+ integrals[:, a_slice, b_slice], axes=(0, 2, 1)
643
+ )
644
+
645
+ # Return plain 2d array if components is set to 0, i.e., remove first axis.
646
+ if is_2d:
647
+ integrals = np.squeeze(integrals, axis=0)
648
+
649
+ # Reordering will be disabled, when spherical integrals are desired. They
650
+ # are reordered outside of this function. Reordering them already here
651
+ # would mess up the results after the 2nd reordering.
652
+ if can_reorder and self.ordering == "native":
653
+ integrals = np.einsum(
654
+ "ij,...jk,kl->...il",
655
+ self.P_cart,
656
+ integrals,
657
+ shells_b.P_cart.T,
658
+ optimize="greedy",
659
+ )
660
+ return integrals
661
+
662
+ def get_1el_ints_sph(
663
+ self, int_func=None, int_matrix=None, other=None, **kwargs
664
+ ) -> NDArray:
665
+ if int_matrix is None:
666
+ # Disallow reordering of the Cartesian integrals
667
+ int_matrix = self.get_1el_ints_cart(
668
+ int_func, other=other, can_reorder=False, **kwargs
669
+ )
670
+
671
+ shells_b = self if other is None else other
672
+ # Reordering (P) and Cartesian to spherical conversion (C).
673
+ PC_a = self.reorder_c2s_coeffs
674
+ PC_b = shells_b.reorder_c2s_coeffs
675
+ int_matrix_sph = np.einsum(
676
+ "ij,...jk,kl->...il", PC_a, int_matrix, PC_b.T, optimize="greedy"
677
+ )
678
+ return int_matrix_sph
679
+
680
+ def get_2c2el_ints_cart(self):
681
+ return self.get_1el_ints_cart(int2c2e3d.int2c2e3d)
682
+
683
+ def get_2c2el_ints_sph(self):
684
+ return self.get_1el_ints_sph(int2c2e3d.int2c2e3d)
685
+
686
+ def get_3c2el_ints_cart(self, shells_aux):
687
+ """Cartesian 3-center-2-electron integrals.
688
+
689
+ DO NOT USE THESE INTEGRALS AS THEY ARE RETURNED FROM THIS METHOD.
690
+ These integrals utilize recurrence relations that are only valid,
691
+ when the resulting Cartesian integrals are transformed into spherical
692
+ integrals.
693
+
694
+ Contrary to the general function 'get_1el_ints_cart', that supports
695
+ different 'func_dict' arguments and cross-integrals between two
696
+ different shells this function is less general. This function is
697
+ restricted to '_3center2el_sph' and always uses 'self.shells' as well as
698
+ 'self.shells_aux'.
699
+ """
700
+ shells_a = self
701
+ cart_bf_num_a = shells_a.cart_bf_num
702
+ cart_bf_num_aux = shells_aux.cart_bf_num
703
+
704
+ integrals = np.zeros((cart_bf_num_a, cart_bf_num_a, cart_bf_num_aux))
705
+ # func_dict = _3center2el3d_sph._3center2el3d_sph
706
+ func_dict = int3c2e3d_sph.int3c2e3d_sph
707
+
708
+ for i, shell_a in enumerate(shells_a):
709
+ La, A, da, ax, a_ind, a_size = shell_a.as_tuple()
710
+ a_slice = slice(a_ind, a_ind + a_size)
711
+ # As noted in the docstring, we iterate over pairs of self.shells (shells_a)
712
+ for shell_b in shells_a[i:]:
713
+ Lb, B, db, bx, b_ind, b_size = shell_b.as_tuple()
714
+ b_slice = slice(b_ind, b_ind + b_size)
715
+ for shell_c in shells_aux:
716
+ Lc, C, dc, cx, c_ind, c_size = shell_c.as_tuple()
717
+ c_slice = slice(c_ind, c_ind + c_size)
718
+ integrals[a_slice, b_slice, c_slice] = func_dict[(La, Lb, Lc)](
719
+ ax[:, None, None],
720
+ da[:, None, None],
721
+ A,
722
+ bx[None, :, None],
723
+ db[None, :, None],
724
+ B,
725
+ cx[None, None, :],
726
+ dc[None, None, :],
727
+ C,
728
+ )
729
+ integrals[b_slice, a_slice, c_slice] = np.transpose(
730
+ integrals[a_slice, b_slice, c_slice], axes=(1, 0, 2)
731
+ )
732
+ return integrals
733
+
734
+ def get_3c2el_ints_sph(self, shells_aux):
735
+ int_matrix = self.get_3c2el_ints_cart(shells_aux)
736
+
737
+ PC_a = self.reorder_c2s_coeffs
738
+ PC_c = shells_aux.reorder_c2s_coeffs
739
+ int_matrix_sph = np.einsum(
740
+ "ij,jlm,nl,om->ino", PC_a, int_matrix, PC_a, PC_c, optimize="greedy"
741
+ )
742
+ return int_matrix_sph
743
+
744
+ #####################
745
+ # Overlap integrals #
746
+ #####################
747
+
748
+ @staticmethod
749
+ def screen_S(a, b, R, k=10):
750
+ """
751
+ Returns False when distance R is below calculated threshold.
752
+ Derived from the sympy code below.
753
+
754
+ from sympy import *
755
+
756
+ a, b, S, R = symbols("a b S R", real=True, positive=True)
757
+ k = symbols("k", integer=True, positive=True)
758
+ apb = a + b
759
+ # 0 = S - 10**(-k)
760
+ ovlp = (pi / apb)**Rational(3,2) * exp(-a*b / apb * R**2) - 10**(-k)
761
+ print("ss-overlap:", ovlp)
762
+ # Distance R, when ss-overlaps drops below 10**(-k)
763
+ R = solve(ovlp, R)[0]
764
+ print("R:", R)
765
+
766
+ Parameters
767
+ ----------
768
+ a
769
+ Minimum exponent in shell a.
770
+ b
771
+ Minimum exponent in shell b.
772
+ R
773
+ Real space distance of shells a and b.
774
+ """
775
+ return R < (
776
+ sqrt(a + b)
777
+ * sqrt(log(10**k * pi ** (3 / 2) / (a + b) ** (3 / 2)))
778
+ / (sqrt(a) * sqrt(b))
779
+ )
780
+
781
+ def get_S_cart(self, other=None) -> NDArray:
782
+ # return self.get_1el_ints_cart(ovlp, other=other, screen_func=Shells.screen_S)
783
+ return self.get_1el_ints_cart(
784
+ ovlp3d.ovlp3d, other=other, screen_func=Shells.screen_S
785
+ )
786
+
787
+ def get_S_sph(self, other=None) -> NDArray:
788
+ return self.get_1el_ints_sph(
789
+ ovlp3d.ovlp3d, other=other, screen_func=Shells.screen_S
790
+ )
791
+
792
+ @property
793
+ def S_cart(self):
794
+ return self.get_S_cart()
795
+
796
+ @property
797
+ def S_sph(self):
798
+ return self.get_S_sph()
799
+
800
+ ############################
801
+ # Kinetic energy integrals #
802
+ ############################
803
+
804
+ def get_T_cart(self, other=None) -> NDArray:
805
+ # return self.get_1el_ints_cart(kinetic, other=other)
806
+ return self.get_1el_ints_cart(kinetic3d.kinetic3d, other=other)
807
+
808
+ def get_T_sph(self, other=None) -> NDArray:
809
+ return self.get_1el_ints_sph(kinetic3d.kinetic3d, other=other)
810
+
811
+ @property
812
+ def T_cart(self):
813
+ return self.get_T_cart()
814
+
815
+ @property
816
+ def T_sph(self):
817
+ return self.get_T_sph()
818
+
819
+ ################################
820
+ # Nuclear attraction integrals #
821
+ ################################
822
+
823
+ def get_V_cart(self, coords3d=None, charges=None, **kwargs):
824
+ """Nuclear attraction integrals.
825
+
826
+ Coordinates and charges must be either be omitted or given together.
827
+ Alternatively, this function could also take one array that combines
828
+ coords3c and charges (not yet implemented).
829
+ """
830
+ assert ((coords3d is None) and (charges is None)) or (
831
+ (coords3d is not None) and (charges is not None)
832
+ )
833
+ if coords3d is None:
834
+ atoms, coords3d = self.atoms_coords3d
835
+ charges = nuc_charges_for_atoms(atoms)
836
+
837
+ # def boys_1c1el(n_max, ax, A, bx, B, C):
838
+ # ax = ax[:, None]
839
+ # bx = bx[None, :]
840
+ # p = ax + bx
841
+ # P = -(ax * A + bx * B) / p
842
+ # R_PC2 = ((P - C)**2).sum()
843
+ # pR_PC2 = p * R_PC2
844
+ # return np.array([boys.boys(n, pR_PC2) for n in range(n_max+1)])
845
+
846
+ cart_bf_num = self.cart_bf_num
847
+ V_nuc = np.zeros((cart_bf_num, cart_bf_num))
848
+ # Loop over all centers and add their contributions
849
+ for R, Z in zip(coords3d, charges):
850
+ # -Z = -1 * Z, because electrons have negative charge.
851
+ V_nuc += -Z * self.get_1el_ints_cart(
852
+ coulomb3d.coulomb3d,
853
+ R=R,
854
+ **kwargs,
855
+ )
856
+ return V_nuc
857
+
858
+ def get_V_sph(self, coords3d=None, charges=None) -> NDArray:
859
+ V_cart = self.get_V_cart(coords3d, charges, can_reorder=False)
860
+ return self.get_1el_ints_sph(int_func=None, int_matrix=V_cart)
861
+
862
+ @property
863
+ def V_cart(self):
864
+ return self.get_V_cart()
865
+
866
+ @property
867
+ def V_sph(self):
868
+ return self.get_V_sph()
869
+
870
+ ###########################
871
+ # Dipole moment integrals #
872
+ ###########################
873
+
874
+ def get_dipole_ints_cart(self, origin):
875
+ return self.get_1el_ints_cart(
876
+ dipole3d.dipole3d, components=3, R=origin, screen_func=Shells.screen_S
877
+ )
878
+
879
+ def get_dipole_ints_sph(self, origin) -> NDArray:
880
+ return self.get_1el_ints_sph(
881
+ dipole3d.dipole3d, components=3, R=origin, screen_func=Shells.screen_S
882
+ )
883
+
884
+ ##################################################
885
+ # Quadrupole moment integrals, diagonal elements #
886
+ ##################################################
887
+
888
+ def get_diag_quadrupole_ints_cart(self, origin):
889
+ return self.get_1el_ints_cart(
890
+ diag_quadrupole3d.diag_quadrupole3d,
891
+ components=3,
892
+ R=origin,
893
+ screen_func=Shells.screen_S,
894
+ )
895
+
896
+ def get_diag_quadrupole_ints_sph(self, origin):
897
+ return self.get_1el_ints_sph(
898
+ diag_quadrupole3d.diag_quadrupole3d,
899
+ components=3,
900
+ R=origin,
901
+ screen_func=Shells.screen_S,
902
+ )
903
+
904
+ ###############################
905
+ # Quadrupole moment integrals #
906
+ ###############################
907
+
908
+ def get_quadrupole_ints_cart(self, origin):
909
+ ints_flat = self.get_1el_ints_cart(
910
+ quadrupole3d.quadrupole3d,
911
+ components=6,
912
+ R=origin,
913
+ screen_func=Shells.screen_S,
914
+ )
915
+ return multi_component_sym_mat(ints_flat, 3)
916
+
917
+ def get_quadrupole_ints_sph(self, origin) -> NDArray:
918
+ ints_flat = self.get_1el_ints_sph(
919
+ quadrupole3d.quadrupole3d,
920
+ components=6,
921
+ R=origin,
922
+ screen_func=Shells.screen_S,
923
+ )
924
+ return multi_component_sym_mat(ints_flat, 3)
925
+
926
+ def to_sap_shells(self):
927
+ for shell in self.shells:
928
+ shell.to_sap_shell()
929
+
930
+ def __str__(self):
931
+ return f"{self.__class__.__name__}({len(self.shells)} shells, ordering={self.ordering})"
932
+
933
+
934
+ class AOMixShells(Shells):
935
+ cart_order = (
936
+ ("",),
937
+ ("x", "y", "z"),
938
+ ("xx", "yy", "zz", "xy", "xz", "yz"),
939
+ ("xxx", "yyy", "zzz", "xyy", "xxy", "xxz", "xzz", "yzz", "yyz", "xyz"),
940
+ (
941
+ "zzzz",
942
+ "yzzz",
943
+ "yyzz",
944
+ "yyyz",
945
+ "yyyy",
946
+ "xzzz",
947
+ "xyzz",
948
+ "xyyz",
949
+ "xyyy",
950
+ "xxzz",
951
+ "xxyz",
952
+ "xxyy",
953
+ "xxxz",
954
+ "xxxy",
955
+ "xxxx",
956
+ ),
957
+ )
958
+
959
+
960
+ class ORCAShells(Shells):
961
+ sph_Ps = {
962
+ 0: [[1]], # s
963
+ 1: [[0, 1, 0], [1, 0, 0], [0, 0, 1]], # pz px py
964
+ 2: [
965
+ [0, 0, 1, 0, 0], # dz²
966
+ [0, 1, 0, 0, 0], # dxz
967
+ [0, 0, 0, 1, 0], # dyz
968
+ [1, 0, 0, 0, 0], # dx² - y²
969
+ [0, 0, 0, 0, 1], # dxy
970
+ ],
971
+ 3: [
972
+ [0, 0, 0, 1, 0, 0, 0],
973
+ [0, 0, 1, 0, 0, 0, 0],
974
+ [0, 0, 0, 0, 1, 0, 0],
975
+ [0, 1, 0, 0, 0, 0, 0],
976
+ [0, 0, 0, 0, 0, 1, 0],
977
+ [-1, 0, 0, 0, 0, 0, 0], # ORCA, why you do this to me?
978
+ [0, 0, 0, 0, 0, 0, -1], # sign flip, as in the line above
979
+ ],
980
+ 4: [
981
+ [0, 0, 0, 0, 1, 0, 0, 0, 0],
982
+ [0, 0, 0, 1, 0, 0, 0, 0, 0],
983
+ [0, 0, 0, 0, 0, 1, 0, 0, 0],
984
+ [0, 0, 1, 0, 0, 0, 0, 0, 0],
985
+ [0, 0, 0, 0, 0, 0, 1, 0, 0],
986
+ [0, -1, 0, 0, 0, 0, 0, 0, 0], # sign flip
987
+ [0, 0, 0, 0, 0, 0, 0, -1, 0], # sign flip
988
+ [-1, 0, 0, 0, 0, 0, 0, 0, 0], # sign flip
989
+ [0, 0, 0, 0, 0, 0, 0, 0, -1], # sign flip
990
+ ],
991
+ }
992
+
993
+
994
+ class FCHKShells(Shells):
995
+ cart_order = (
996
+ ("",),
997
+ ("x", "y", "z"),
998
+ ("xx", "yy", "zz", "xy", "xz", "yz"),
999
+ ("xxx", "yyy", "zzz", "xyy", "xxy", "xxz", "xzz", "yzz", "yyz", "xyz"),
1000
+ (
1001
+ "zzzz",
1002
+ "yzzz",
1003
+ "yyzz",
1004
+ "yyyz",
1005
+ "yyyy",
1006
+ "xzzz",
1007
+ "xyzz",
1008
+ "xyyz",
1009
+ "xyyy",
1010
+ "xxzz",
1011
+ "xxyz",
1012
+ "xxyy",
1013
+ "xxxz",
1014
+ "xxxy",
1015
+ "xxxx",
1016
+ ),
1017
+ )
1018
+
1019
+ sph_Ps = {
1020
+ 0: [[1]], # s
1021
+ 1: [[1, 0, 0], [0, 0, 1], [0, 1, 0]], # px pz py
1022
+ 2: [
1023
+ [0, 0, 1, 0, 0], # dz²
1024
+ [0, 1, 0, 0, 0], # dxz
1025
+ [0, 0, 0, 1, 0], # dyz
1026
+ [1, 0, 0, 0, 0], # dx² - y²
1027
+ [0, 0, 0, 0, 1], # dxy
1028
+ ],
1029
+ # Similar to ORCA, but w/o the sign flips ;)
1030
+ 3: [
1031
+ [0, 0, 0, 1, 0, 0, 0],
1032
+ [0, 0, 1, 0, 0, 0, 0],
1033
+ [0, 0, 0, 0, 1, 0, 0],
1034
+ [0, 1, 0, 0, 0, 0, 0],
1035
+ [0, 0, 0, 0, 0, 1, 0],
1036
+ [1, 0, 0, 0, 0, 0, 0],
1037
+ [0, 0, 0, 0, 0, 0, 1],
1038
+ ],
1039
+ 4: [
1040
+ [0, 0, 0, 0, 1, 0, 0, 0, 0],
1041
+ [0, 0, 0, 1, 0, 0, 0, 0, 0],
1042
+ [0, 0, 0, 0, 0, 1, 0, 0, 0],
1043
+ [0, 0, 1, 0, 0, 0, 0, 0, 0],
1044
+ [0, 0, 0, 0, 0, 0, 1, 0, 0],
1045
+ [0, 1, 0, 0, 0, 0, 0, 0, 0],
1046
+ [0, 0, 0, 0, 0, 0, 0, 1, 0],
1047
+ [1, 0, 0, 0, 0, 0, 0, 0, 0],
1048
+ [0, 0, 0, 0, 0, 0, 0, 0, 1],
1049
+ ],
1050
+ }
1051
+
1052
+
1053
+ class MoldenShells(Shells):
1054
+ sph_Ps = {
1055
+ 0: [[1]], # s
1056
+ 1: [[1, 0, 0], [0, 0, 1], [0, 1, 0]], # px, py, pz
1057
+ 2: [
1058
+ [0, 0, 1, 0, 0], # dz²
1059
+ [0, 1, 0, 0, 0], # dxz
1060
+ [0, 0, 0, 1, 0], # dyz
1061
+ [1, 0, 0, 0, 0], # dx² - y²
1062
+ [0, 0, 0, 0, 1], # dxy
1063
+ ],
1064
+ 3: [
1065
+ [0, 0, 0, 1, 0, 0, 0],
1066
+ [0, 0, 1, 0, 0, 0, 0],
1067
+ [0, 0, 0, 0, 1, 0, 0],
1068
+ [0, 1, 0, 0, 0, 0, 0],
1069
+ [0, 0, 0, 0, 0, 1, 0],
1070
+ [1, 0, 0, 0, 0, 0, 0],
1071
+ [0, 0, 0, 0, 0, 0, 1],
1072
+ ],
1073
+ 4: [
1074
+ [0, 0, 0, 0, 1, 0, 0, 0, 0],
1075
+ [0, 0, 0, 1, 0, 0, 0, 0, 0],
1076
+ [0, 0, 0, 0, 0, 1, 0, 0, 0],
1077
+ [0, 0, 1, 0, 0, 0, 0, 0, 0],
1078
+ [0, 0, 0, 0, 0, 0, 1, 0, 0],
1079
+ [0, 1, 0, 0, 0, 0, 0, 0, 0],
1080
+ [0, 0, 0, 0, 0, 0, 0, 1, 0],
1081
+ [1, 0, 0, 0, 0, 0, 0, 0, 0],
1082
+ [0, 0, 0, 0, 0, 0, 0, 0, 1],
1083
+ ],
1084
+ }
1085
+
1086
+
1087
+ class ORCAMoldenShells(Shells):
1088
+ sph_Ps = {
1089
+ 0: [[1]], # s
1090
+ 1: [[1, 0, 0], [0, 0, 1], [0, 1, 0]], # px, py, pz
1091
+ 2: [
1092
+ [0, 0, 1, 0, 0], # dz²
1093
+ [0, 1, 0, 0, 0], # dxz
1094
+ [0, 0, 0, 1, 0], # dyz
1095
+ [1, 0, 0, 0, 0], # dx² - y²
1096
+ [0, 0, 0, 0, 1], # dxy
1097
+ ],
1098
+ 3: [
1099
+ [0, 0, 0, 1, 0, 0, 0],
1100
+ [0, 0, 1, 0, 0, 0, 0],
1101
+ [0, 0, 0, 0, 1, 0, 0],
1102
+ [0, 1, 0, 0, 0, 0, 0],
1103
+ [0, 0, 0, 0, 0, 1, 0],
1104
+ [-1, 0, 0, 0, 0, 0, 0],
1105
+ [0, 0, 0, 0, 0, 0, -1],
1106
+ ],
1107
+ 4: [
1108
+ [0, 0, 0, 0, 1, 0, 0, 0, 0],
1109
+ [0, 0, 0, 1, 0, 0, 0, 0, 0],
1110
+ [0, 0, 0, 0, 0, 1, 0, 0, 0],
1111
+ [0, 0, 1, 0, 0, 0, 0, 0, 0],
1112
+ [0, 0, 0, 0, 0, 0, 1, 0, 0],
1113
+ [0, -1, 0, 0, 0, 0, 0, 0, 0],
1114
+ [0, 0, 0, 0, 0, 0, 0, -1, 0],
1115
+ [-1, 0, 0, 0, 0, 0, 0, 0, 0],
1116
+ [0, 0, 0, 0, 0, 0, 0, 0, -1],
1117
+ ],
1118
+ }
1119
+
1120
+
1121
+ def pyscf_cart_order(l):
1122
+ order = list()
1123
+ for lx in reversed(range(l + 1)):
1124
+ for ly in reversed(range(l + 1 - lx)):
1125
+ lz = l - lx - ly
1126
+ order.append("x" * lx + "y" * ly + "z" * lz)
1127
+ return tuple(order)
1128
+
1129
+
1130
+ class PySCFShells(Shells):
1131
+
1132
+ """
1133
+ Cartesian bfs >= d angular momentum are not normalized!
1134
+ S_ref = mol.intor("int1e_ovlp_cart")
1135
+ N = 1 / np.diag(S_ref)**0.5
1136
+ ao *= N
1137
+ """
1138
+
1139
+ cart_order = [pyscf_cart_order(l) for l in range(5)]
1140
+
1141
+ sph_Ps = {
1142
+ 0: [[1]], # s
1143
+ 1: [[1, 0, 0], [0, 0, 1], [0, 1, 0]], # px py pz
1144
+ 2: [
1145
+ [0, 0, 0, 0, 1], # dxy
1146
+ [0, 0, 0, 1, 0], # dyz
1147
+ [0, 0, 1, 0, 0], # dz²
1148
+ [0, 1, 0, 0, 0], # dxz
1149
+ [1, 0, 0, 0, 0], # dx² - y²
1150
+ ],
1151
+ 3: [
1152
+ [0, 0, 0, 0, 0, 0, 1],
1153
+ [0, 0, 0, 0, 0, 1, 0],
1154
+ [0, 0, 0, 0, 1, 0, 0],
1155
+ [0, 0, 0, 1, 0, 0, 0],
1156
+ [0, 0, 1, 0, 0, 0, 0],
1157
+ [0, 1, 0, 0, 0, 0, 0],
1158
+ [1, 0, 0, 0, 0, 0, 0],
1159
+ ],
1160
+ 4: [
1161
+ [0, 0, 0, 0, 0, 0, 0, 0, 1],
1162
+ [0, 0, 0, 0, 0, 0, 0, 1, 0],
1163
+ [0, 0, 0, 0, 0, 0, 1, 0, 0],
1164
+ [0, 0, 0, 0, 0, 1, 0, 0, 0],
1165
+ [0, 0, 0, 0, 1, 0, 0, 0, 0],
1166
+ [0, 0, 0, 1, 0, 0, 0, 0, 0],
1167
+ [0, 0, 1, 0, 0, 0, 0, 0, 0],
1168
+ [0, 1, 0, 0, 0, 0, 0, 0, 0],
1169
+ [1, 0, 0, 0, 0, 0, 0, 0, 0],
1170
+ ],
1171
+ }