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.
- hessian_ff/__init__.py +50 -0
- hessian_ff/analytical_hessian.py +609 -0
- hessian_ff/constants.py +46 -0
- hessian_ff/forcefield.py +339 -0
- hessian_ff/loaders.py +608 -0
- hessian_ff/native/Makefile +8 -0
- hessian_ff/native/__init__.py +28 -0
- hessian_ff/native/analytical_hessian.py +88 -0
- hessian_ff/native/analytical_hessian_ext.cpp +258 -0
- hessian_ff/native/bonded.py +82 -0
- hessian_ff/native/bonded_ext.cpp +640 -0
- hessian_ff/native/loader.py +349 -0
- hessian_ff/native/nonbonded.py +118 -0
- hessian_ff/native/nonbonded_ext.cpp +1150 -0
- hessian_ff/prmtop_parmed.py +23 -0
- hessian_ff/system.py +107 -0
- hessian_ff/terms/__init__.py +14 -0
- hessian_ff/terms/angle.py +73 -0
- hessian_ff/terms/bond.py +44 -0
- hessian_ff/terms/cmap.py +406 -0
- hessian_ff/terms/dihedral.py +141 -0
- hessian_ff/terms/nonbonded.py +209 -0
- hessian_ff/tests/__init__.py +0 -0
- hessian_ff/tests/conftest.py +75 -0
- hessian_ff/tests/data/small/complex.parm7 +1346 -0
- hessian_ff/tests/data/small/complex.pdb +125 -0
- hessian_ff/tests/data/small/complex.rst7 +63 -0
- hessian_ff/tests/test_coords_input.py +44 -0
- hessian_ff/tests/test_energy_force.py +49 -0
- hessian_ff/tests/test_hessian.py +137 -0
- hessian_ff/tests/test_smoke.py +18 -0
- hessian_ff/tests/test_validation.py +40 -0
- hessian_ff/workflows.py +889 -0
- mlmm/__init__.py +36 -0
- mlmm/__main__.py +7 -0
- mlmm/_version.py +34 -0
- mlmm/add_elem_info.py +374 -0
- mlmm/advanced_help.py +91 -0
- mlmm/align_freeze_atoms.py +601 -0
- mlmm/all.py +3535 -0
- mlmm/bond_changes.py +231 -0
- mlmm/bool_compat.py +223 -0
- mlmm/cli.py +574 -0
- mlmm/cli_utils.py +166 -0
- mlmm/default_group.py +337 -0
- mlmm/defaults.py +467 -0
- mlmm/define_layer.py +526 -0
- mlmm/dft.py +1041 -0
- mlmm/energy_diagram.py +253 -0
- mlmm/extract.py +2213 -0
- mlmm/fix_altloc.py +464 -0
- mlmm/freq.py +1406 -0
- mlmm/harmonic_constraints.py +140 -0
- mlmm/hessian_cache.py +44 -0
- mlmm/hessian_calc.py +174 -0
- mlmm/irc.py +638 -0
- mlmm/mlmm_calc.py +2262 -0
- mlmm/mm_parm.py +945 -0
- mlmm/oniom_export.py +1983 -0
- mlmm/oniom_import.py +457 -0
- mlmm/opt.py +1742 -0
- mlmm/path_opt.py +1353 -0
- mlmm/path_search.py +2299 -0
- mlmm/preflight.py +88 -0
- mlmm/py.typed +1 -0
- mlmm/pysis_runner.py +45 -0
- mlmm/scan.py +1047 -0
- mlmm/scan2d.py +1226 -0
- mlmm/scan3d.py +1265 -0
- mlmm/scan_common.py +184 -0
- mlmm/summary_log.py +736 -0
- mlmm/trj2fig.py +448 -0
- mlmm/tsopt.py +2871 -0
- mlmm/utils.py +2309 -0
- mlmm/xtb_embedcharge_correction.py +475 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/METADATA +1159 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/RECORD +372 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/WHEEL +5 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/entry_points.txt +2 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/licenses/LICENSE +674 -0
- mlmm_toolkit-0.2.2.dev0.dist-info/top_level.txt +4 -0
- pysisyphus/Geometry.py +1667 -0
- pysisyphus/LICENSE +674 -0
- pysisyphus/TableFormatter.py +63 -0
- pysisyphus/TablePrinter.py +74 -0
- pysisyphus/__init__.py +12 -0
- pysisyphus/calculators/AFIR.py +452 -0
- pysisyphus/calculators/AnaPot.py +20 -0
- pysisyphus/calculators/AnaPot2.py +48 -0
- pysisyphus/calculators/AnaPot3.py +12 -0
- pysisyphus/calculators/AnaPot4.py +20 -0
- pysisyphus/calculators/AnaPotBase.py +337 -0
- pysisyphus/calculators/AnaPotCBM.py +25 -0
- pysisyphus/calculators/AtomAtomTransTorque.py +154 -0
- pysisyphus/calculators/CFOUR.py +250 -0
- pysisyphus/calculators/Calculator.py +844 -0
- pysisyphus/calculators/CerjanMiller.py +24 -0
- pysisyphus/calculators/Composite.py +123 -0
- pysisyphus/calculators/ConicalIntersection.py +171 -0
- pysisyphus/calculators/DFTBp.py +430 -0
- pysisyphus/calculators/DFTD3.py +66 -0
- pysisyphus/calculators/DFTD4.py +84 -0
- pysisyphus/calculators/Dalton.py +61 -0
- pysisyphus/calculators/Dimer.py +681 -0
- pysisyphus/calculators/Dummy.py +20 -0
- pysisyphus/calculators/EGO.py +76 -0
- pysisyphus/calculators/EnergyMin.py +224 -0
- pysisyphus/calculators/ExternalPotential.py +264 -0
- pysisyphus/calculators/FakeASE.py +35 -0
- pysisyphus/calculators/FourWellAnaPot.py +28 -0
- pysisyphus/calculators/FreeEndNEBPot.py +39 -0
- pysisyphus/calculators/Gaussian09.py +18 -0
- pysisyphus/calculators/Gaussian16.py +726 -0
- pysisyphus/calculators/HardSphere.py +159 -0
- pysisyphus/calculators/IDPPCalculator.py +49 -0
- pysisyphus/calculators/IPIClient.py +133 -0
- pysisyphus/calculators/IPIServer.py +234 -0
- pysisyphus/calculators/LEPSBase.py +24 -0
- pysisyphus/calculators/LEPSExpr.py +139 -0
- pysisyphus/calculators/LennardJones.py +80 -0
- pysisyphus/calculators/MOPAC.py +219 -0
- pysisyphus/calculators/MullerBrownSympyPot.py +51 -0
- pysisyphus/calculators/MultiCalc.py +85 -0
- pysisyphus/calculators/NFK.py +45 -0
- pysisyphus/calculators/OBabel.py +87 -0
- pysisyphus/calculators/ONIOMv2.py +1129 -0
- pysisyphus/calculators/ORCA.py +893 -0
- pysisyphus/calculators/ORCA5.py +6 -0
- pysisyphus/calculators/OpenMM.py +88 -0
- pysisyphus/calculators/OpenMolcas.py +281 -0
- pysisyphus/calculators/OverlapCalculator.py +908 -0
- pysisyphus/calculators/Psi4.py +218 -0
- pysisyphus/calculators/PyPsi4.py +37 -0
- pysisyphus/calculators/PySCF.py +341 -0
- pysisyphus/calculators/PyXTB.py +73 -0
- pysisyphus/calculators/QCEngine.py +106 -0
- pysisyphus/calculators/Rastrigin.py +22 -0
- pysisyphus/calculators/Remote.py +76 -0
- pysisyphus/calculators/Rosenbrock.py +15 -0
- pysisyphus/calculators/SocketCalc.py +97 -0
- pysisyphus/calculators/TIP3P.py +111 -0
- pysisyphus/calculators/TransTorque.py +161 -0
- pysisyphus/calculators/Turbomole.py +965 -0
- pysisyphus/calculators/VRIPot.py +37 -0
- pysisyphus/calculators/WFOWrapper.py +333 -0
- pysisyphus/calculators/WFOWrapper2.py +341 -0
- pysisyphus/calculators/XTB.py +418 -0
- pysisyphus/calculators/__init__.py +81 -0
- pysisyphus/calculators/cosmo_data.py +139 -0
- pysisyphus/calculators/parser.py +150 -0
- pysisyphus/color.py +19 -0
- pysisyphus/config.py +133 -0
- pysisyphus/constants.py +65 -0
- pysisyphus/cos/AdaptiveNEB.py +230 -0
- pysisyphus/cos/ChainOfStates.py +725 -0
- pysisyphus/cos/FreeEndNEB.py +25 -0
- pysisyphus/cos/FreezingString.py +103 -0
- pysisyphus/cos/GrowingChainOfStates.py +71 -0
- pysisyphus/cos/GrowingNT.py +309 -0
- pysisyphus/cos/GrowingString.py +508 -0
- pysisyphus/cos/NEB.py +189 -0
- pysisyphus/cos/SimpleZTS.py +64 -0
- pysisyphus/cos/__init__.py +22 -0
- pysisyphus/cos/stiffness.py +199 -0
- pysisyphus/drivers/__init__.py +17 -0
- pysisyphus/drivers/afir.py +855 -0
- pysisyphus/drivers/barriers.py +271 -0
- pysisyphus/drivers/birkholz.py +138 -0
- pysisyphus/drivers/cluster.py +318 -0
- pysisyphus/drivers/diabatization.py +133 -0
- pysisyphus/drivers/merge.py +368 -0
- pysisyphus/drivers/merge_mol2.py +322 -0
- pysisyphus/drivers/opt.py +375 -0
- pysisyphus/drivers/perf.py +91 -0
- pysisyphus/drivers/pka.py +52 -0
- pysisyphus/drivers/precon_pos_rot.py +669 -0
- pysisyphus/drivers/rates.py +480 -0
- pysisyphus/drivers/replace.py +219 -0
- pysisyphus/drivers/scan.py +212 -0
- pysisyphus/drivers/spectrum.py +166 -0
- pysisyphus/drivers/thermo.py +31 -0
- pysisyphus/dynamics/Gaussian.py +103 -0
- pysisyphus/dynamics/__init__.py +20 -0
- pysisyphus/dynamics/colvars.py +136 -0
- pysisyphus/dynamics/driver.py +297 -0
- pysisyphus/dynamics/helpers.py +256 -0
- pysisyphus/dynamics/lincs.py +105 -0
- pysisyphus/dynamics/mdp.py +364 -0
- pysisyphus/dynamics/rattle.py +121 -0
- pysisyphus/dynamics/thermostats.py +128 -0
- pysisyphus/dynamics/wigner.py +266 -0
- pysisyphus/elem_data.py +3473 -0
- pysisyphus/exceptions.py +2 -0
- pysisyphus/filtertrj.py +69 -0
- pysisyphus/helpers.py +623 -0
- pysisyphus/helpers_pure.py +649 -0
- pysisyphus/init_logging.py +50 -0
- pysisyphus/intcoords/Bend.py +69 -0
- pysisyphus/intcoords/Bend2.py +25 -0
- pysisyphus/intcoords/BondedFragment.py +32 -0
- pysisyphus/intcoords/Cartesian.py +41 -0
- pysisyphus/intcoords/CartesianCoords.py +140 -0
- pysisyphus/intcoords/Coords.py +56 -0
- pysisyphus/intcoords/DLC.py +197 -0
- pysisyphus/intcoords/DistanceFunction.py +34 -0
- pysisyphus/intcoords/DummyImproper.py +70 -0
- pysisyphus/intcoords/DummyTorsion.py +72 -0
- pysisyphus/intcoords/LinearBend.py +105 -0
- pysisyphus/intcoords/LinearDisplacement.py +80 -0
- pysisyphus/intcoords/OutOfPlane.py +59 -0
- pysisyphus/intcoords/PrimTypes.py +286 -0
- pysisyphus/intcoords/Primitive.py +137 -0
- pysisyphus/intcoords/RedundantCoords.py +659 -0
- pysisyphus/intcoords/RobustTorsion.py +59 -0
- pysisyphus/intcoords/Rotation.py +147 -0
- pysisyphus/intcoords/Stretch.py +31 -0
- pysisyphus/intcoords/Torsion.py +101 -0
- pysisyphus/intcoords/Torsion2.py +25 -0
- pysisyphus/intcoords/Translation.py +45 -0
- pysisyphus/intcoords/__init__.py +61 -0
- pysisyphus/intcoords/augment_bonds.py +126 -0
- pysisyphus/intcoords/derivatives.py +10512 -0
- pysisyphus/intcoords/eval.py +80 -0
- pysisyphus/intcoords/exceptions.py +37 -0
- pysisyphus/intcoords/findiffs.py +48 -0
- pysisyphus/intcoords/generate_derivatives.py +414 -0
- pysisyphus/intcoords/helpers.py +235 -0
- pysisyphus/intcoords/logging_conf.py +10 -0
- pysisyphus/intcoords/mp_derivatives.py +10836 -0
- pysisyphus/intcoords/setup.py +962 -0
- pysisyphus/intcoords/setup_fast.py +176 -0
- pysisyphus/intcoords/update.py +272 -0
- pysisyphus/intcoords/valid.py +89 -0
- pysisyphus/interpolate/Geodesic.py +93 -0
- pysisyphus/interpolate/IDPP.py +55 -0
- pysisyphus/interpolate/Interpolator.py +116 -0
- pysisyphus/interpolate/LST.py +70 -0
- pysisyphus/interpolate/Redund.py +152 -0
- pysisyphus/interpolate/__init__.py +9 -0
- pysisyphus/interpolate/helpers.py +34 -0
- pysisyphus/io/__init__.py +22 -0
- pysisyphus/io/aomix.py +178 -0
- pysisyphus/io/cjson.py +24 -0
- pysisyphus/io/crd.py +101 -0
- pysisyphus/io/cube.py +220 -0
- pysisyphus/io/fchk.py +184 -0
- pysisyphus/io/hdf5.py +49 -0
- pysisyphus/io/hessian.py +72 -0
- pysisyphus/io/mol2.py +146 -0
- pysisyphus/io/molden.py +293 -0
- pysisyphus/io/orca.py +189 -0
- pysisyphus/io/pdb.py +269 -0
- pysisyphus/io/psf.py +79 -0
- pysisyphus/io/pubchem.py +31 -0
- pysisyphus/io/qcschema.py +34 -0
- pysisyphus/io/sdf.py +29 -0
- pysisyphus/io/xyz.py +61 -0
- pysisyphus/io/zmat.py +175 -0
- pysisyphus/irc/DWI.py +108 -0
- pysisyphus/irc/DampedVelocityVerlet.py +134 -0
- pysisyphus/irc/Euler.py +22 -0
- pysisyphus/irc/EulerPC.py +345 -0
- pysisyphus/irc/GonzalezSchlegel.py +187 -0
- pysisyphus/irc/IMKMod.py +164 -0
- pysisyphus/irc/IRC.py +878 -0
- pysisyphus/irc/IRCDummy.py +10 -0
- pysisyphus/irc/Instanton.py +307 -0
- pysisyphus/irc/LQA.py +53 -0
- pysisyphus/irc/ModeKill.py +136 -0
- pysisyphus/irc/ParamPlot.py +53 -0
- pysisyphus/irc/RK4.py +36 -0
- pysisyphus/irc/__init__.py +31 -0
- pysisyphus/irc/initial_displ.py +219 -0
- pysisyphus/linalg.py +411 -0
- pysisyphus/line_searches/Backtracking.py +88 -0
- pysisyphus/line_searches/HagerZhang.py +184 -0
- pysisyphus/line_searches/LineSearch.py +232 -0
- pysisyphus/line_searches/StrongWolfe.py +108 -0
- pysisyphus/line_searches/__init__.py +9 -0
- pysisyphus/line_searches/interpol.py +15 -0
- pysisyphus/modefollow/NormalMode.py +40 -0
- pysisyphus/modefollow/__init__.py +10 -0
- pysisyphus/modefollow/davidson.py +199 -0
- pysisyphus/modefollow/lanczos.py +95 -0
- pysisyphus/optimizers/BFGS.py +99 -0
- pysisyphus/optimizers/BacktrackingOptimizer.py +113 -0
- pysisyphus/optimizers/ConjugateGradient.py +98 -0
- pysisyphus/optimizers/CubicNewton.py +75 -0
- pysisyphus/optimizers/FIRE.py +113 -0
- pysisyphus/optimizers/HessianOptimizer.py +1176 -0
- pysisyphus/optimizers/LBFGS.py +228 -0
- pysisyphus/optimizers/LayerOpt.py +411 -0
- pysisyphus/optimizers/MicroOptimizer.py +169 -0
- pysisyphus/optimizers/NCOptimizer.py +90 -0
- pysisyphus/optimizers/Optimizer.py +1084 -0
- pysisyphus/optimizers/PreconLBFGS.py +260 -0
- pysisyphus/optimizers/PreconSteepestDescent.py +7 -0
- pysisyphus/optimizers/QuickMin.py +74 -0
- pysisyphus/optimizers/RFOptimizer.py +181 -0
- pysisyphus/optimizers/RSA.py +99 -0
- pysisyphus/optimizers/StabilizedQNMethod.py +248 -0
- pysisyphus/optimizers/SteepestDescent.py +23 -0
- pysisyphus/optimizers/StringOptimizer.py +173 -0
- pysisyphus/optimizers/__init__.py +41 -0
- pysisyphus/optimizers/closures.py +301 -0
- pysisyphus/optimizers/cls_map.py +58 -0
- pysisyphus/optimizers/exceptions.py +6 -0
- pysisyphus/optimizers/gdiis.py +280 -0
- pysisyphus/optimizers/guess_hessians.py +311 -0
- pysisyphus/optimizers/hessian_updates.py +355 -0
- pysisyphus/optimizers/poly_fit.py +285 -0
- pysisyphus/optimizers/precon.py +153 -0
- pysisyphus/optimizers/restrict_step.py +24 -0
- pysisyphus/pack.py +172 -0
- pysisyphus/peakdetect.py +948 -0
- pysisyphus/plot.py +1031 -0
- pysisyphus/run.py +2106 -0
- pysisyphus/socket_helper.py +74 -0
- pysisyphus/stocastic/FragmentKick.py +132 -0
- pysisyphus/stocastic/Kick.py +81 -0
- pysisyphus/stocastic/Pipeline.py +303 -0
- pysisyphus/stocastic/__init__.py +21 -0
- pysisyphus/stocastic/align.py +127 -0
- pysisyphus/testing.py +96 -0
- pysisyphus/thermo.py +156 -0
- pysisyphus/trj.py +824 -0
- pysisyphus/tsoptimizers/RSIRFOptimizer.py +56 -0
- pysisyphus/tsoptimizers/RSPRFOptimizer.py +182 -0
- pysisyphus/tsoptimizers/TRIM.py +59 -0
- pysisyphus/tsoptimizers/TSHessianOptimizer.py +463 -0
- pysisyphus/tsoptimizers/__init__.py +23 -0
- pysisyphus/wavefunction/Basis.py +239 -0
- pysisyphus/wavefunction/DIIS.py +76 -0
- pysisyphus/wavefunction/__init__.py +25 -0
- pysisyphus/wavefunction/build_ext.py +42 -0
- pysisyphus/wavefunction/cart2sph.py +190 -0
- pysisyphus/wavefunction/diabatization.py +304 -0
- pysisyphus/wavefunction/excited_states.py +435 -0
- pysisyphus/wavefunction/gen_ints.py +1811 -0
- pysisyphus/wavefunction/helpers.py +104 -0
- pysisyphus/wavefunction/ints/__init__.py +0 -0
- pysisyphus/wavefunction/ints/boys.py +193 -0
- pysisyphus/wavefunction/ints/boys_table_N_64_xasym_27.1_step_0.01.npy +0 -0
- pysisyphus/wavefunction/ints/cart_gto3d.py +176 -0
- pysisyphus/wavefunction/ints/coulomb3d.py +25928 -0
- pysisyphus/wavefunction/ints/diag_quadrupole3d.py +10036 -0
- pysisyphus/wavefunction/ints/dipole3d.py +8762 -0
- pysisyphus/wavefunction/ints/int2c2e3d.py +7198 -0
- pysisyphus/wavefunction/ints/int3c2e3d_sph.py +65040 -0
- pysisyphus/wavefunction/ints/kinetic3d.py +8240 -0
- pysisyphus/wavefunction/ints/ovlp3d.py +3777 -0
- pysisyphus/wavefunction/ints/quadrupole3d.py +15054 -0
- pysisyphus/wavefunction/ints/self_ovlp3d.py +198 -0
- pysisyphus/wavefunction/localization.py +458 -0
- pysisyphus/wavefunction/multipole.py +159 -0
- pysisyphus/wavefunction/normalization.py +36 -0
- pysisyphus/wavefunction/pop_analysis.py +134 -0
- pysisyphus/wavefunction/shells.py +1171 -0
- pysisyphus/wavefunction/wavefunction.py +504 -0
- pysisyphus/wrapper/__init__.py +11 -0
- pysisyphus/wrapper/exceptions.py +2 -0
- pysisyphus/wrapper/jmol.py +120 -0
- pysisyphus/wrapper/mwfn.py +169 -0
- pysisyphus/wrapper/packmol.py +71 -0
- pysisyphus/xyzloader.py +168 -0
- pysisyphus/yaml_mods.py +45 -0
- thermoanalysis/LICENSE +674 -0
- thermoanalysis/QCData.py +244 -0
- thermoanalysis/__init__.py +0 -0
- thermoanalysis/config.py +3 -0
- thermoanalysis/constants.py +20 -0
- thermoanalysis/thermo.py +1011 -0
|
@@ -0,0 +1,1811 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# [1] https://doi.org/10.1063/1.450106
|
|
4
|
+
# Efficient recursive computation of molecular integrals over Cartesian
|
|
5
|
+
# Gaussian functions
|
|
6
|
+
# Obara, Saika, 1986
|
|
7
|
+
# [2] https://doi.org/10.1002/9781119019572
|
|
8
|
+
# Molecular Electronic-Structure Theory
|
|
9
|
+
# Helgaker, Jørgensen, Olsen
|
|
10
|
+
# [3] https://doi.org/10.1021/acs.jctc.7b00788
|
|
11
|
+
# LIBRETA: Computerized Optimization and Code Synthesis for
|
|
12
|
+
# Electron Repulsion Integral Evaluation
|
|
13
|
+
# Jun Zhang
|
|
14
|
+
# [4] https://doi.org/10.1039/B413539C
|
|
15
|
+
# Efficient evaluation of three-center two-electron integrals over Gaussian functions
|
|
16
|
+
# Ahlrichs, 2004
|
|
17
|
+
# [5] EVALUATING MANY-ELECTRON MOLECULAR INTEGRALS FOR QUANTUM CHEMISTRY
|
|
18
|
+
# James Christopher Womack, PhD Thesis
|
|
19
|
+
# [6] https://doi.org/10.1063/1.4983393
|
|
20
|
+
# Efficient evaluation of three-center Coulomb integrals
|
|
21
|
+
# Samu, Kállay, 2017
|
|
22
|
+
# [7] https://arxiv.org/pdf/2210.03192.pdf
|
|
23
|
+
# Memory-Efficient Recursive Evaluation of 3-Center Gaussian Integrals
|
|
24
|
+
# Asadchev, Valeev, 2022
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
from datetime import datetime
|
|
29
|
+
import functools
|
|
30
|
+
import itertools as it
|
|
31
|
+
import os
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
import random
|
|
34
|
+
import string
|
|
35
|
+
import sys
|
|
36
|
+
import textwrap
|
|
37
|
+
import time
|
|
38
|
+
|
|
39
|
+
from jinja2 import Template
|
|
40
|
+
import numpy as np
|
|
41
|
+
from sympy import (
|
|
42
|
+
Array,
|
|
43
|
+
cse,
|
|
44
|
+
exp,
|
|
45
|
+
Expr,
|
|
46
|
+
flatten,
|
|
47
|
+
Function,
|
|
48
|
+
IndexedBase,
|
|
49
|
+
Matrix,
|
|
50
|
+
permutedims,
|
|
51
|
+
pi,
|
|
52
|
+
sqrt,
|
|
53
|
+
Symbol,
|
|
54
|
+
symbols,
|
|
55
|
+
tensorcontraction as tc,
|
|
56
|
+
tensorproduct as tp,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
from sympy.codegen.ast import Assignment
|
|
61
|
+
from sympy.printing.numpy import NumPyPrinter
|
|
62
|
+
from sympy.printing.c import C99CodePrinter
|
|
63
|
+
|
|
64
|
+
# from pysisyphus.wavefunction.cart2sph import cart2sph_coeffs
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
from pysisyphus.config import L_MAX, L_AUX_MAX
|
|
68
|
+
except ModuleNotFoundError:
|
|
69
|
+
L_MAX = 4
|
|
70
|
+
L_AUX_MAX = 5
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
L_MAP = {
|
|
74
|
+
0: "s",
|
|
75
|
+
1: "p",
|
|
76
|
+
2: "d",
|
|
77
|
+
3: "f",
|
|
78
|
+
4: "g",
|
|
79
|
+
5: "h",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
KEYS = (
|
|
83
|
+
"cgto",
|
|
84
|
+
"ovlp",
|
|
85
|
+
"dpm",
|
|
86
|
+
"dqpm",
|
|
87
|
+
"qpm",
|
|
88
|
+
"kin",
|
|
89
|
+
"coul",
|
|
90
|
+
"3c2e",
|
|
91
|
+
"3c2e_sph",
|
|
92
|
+
)
|
|
93
|
+
ONE_THRESH = 1e-14
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def make_py_func(repls, reduced, args=None, name=None, doc_str="", return_array=True):
|
|
97
|
+
if args is None:
|
|
98
|
+
args = list()
|
|
99
|
+
# Generate random name, if no name was supplied
|
|
100
|
+
if name is None:
|
|
101
|
+
name = "func_" + "".join(
|
|
102
|
+
[random.choice(string.ascii_letters) for i in range(8)]
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# This allows using the 'boys' function without producing an error
|
|
106
|
+
print_settings = {
|
|
107
|
+
"allow_unknown_functions": True,
|
|
108
|
+
}
|
|
109
|
+
print_func = NumPyPrinter(print_settings).doprint
|
|
110
|
+
assignments = [Assignment(lhs, rhs) for lhs, rhs in repls]
|
|
111
|
+
py_lines = [print_func(as_) for as_ in assignments]
|
|
112
|
+
return_val = print_func(reduced)
|
|
113
|
+
try:
|
|
114
|
+
n_return_vals = len(reduced)
|
|
115
|
+
except TypeError:
|
|
116
|
+
n_return_vals = 1
|
|
117
|
+
|
|
118
|
+
tpl = Template(
|
|
119
|
+
"""
|
|
120
|
+
def {{ name }}({{ args }}):
|
|
121
|
+
{% if doc_str %}
|
|
122
|
+
\"\"\"{{ doc_str }}\"\"\"
|
|
123
|
+
{% endif %}
|
|
124
|
+
|
|
125
|
+
{% for line in py_lines %}
|
|
126
|
+
{{ line }}
|
|
127
|
+
{% endfor %}
|
|
128
|
+
|
|
129
|
+
{% if return_array %}
|
|
130
|
+
# {{ n_return_vals }} item(s)
|
|
131
|
+
return numpy.array({{ return_val }})
|
|
132
|
+
{% else %}
|
|
133
|
+
return {{ return_val }}
|
|
134
|
+
{% endif %}
|
|
135
|
+
""",
|
|
136
|
+
trim_blocks=True,
|
|
137
|
+
lstrip_blocks=True,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
rendered = textwrap.dedent(
|
|
141
|
+
tpl.render(
|
|
142
|
+
name=name,
|
|
143
|
+
args=args,
|
|
144
|
+
py_lines=py_lines,
|
|
145
|
+
return_val=return_val,
|
|
146
|
+
n_return_vals=n_return_vals,
|
|
147
|
+
return_array=return_array,
|
|
148
|
+
doc_str=doc_str,
|
|
149
|
+
)
|
|
150
|
+
).strip()
|
|
151
|
+
return rendered
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def make_c_func(repls, reduced, args=None, name=None, doc_str=""):
|
|
155
|
+
if args is None:
|
|
156
|
+
args = list()
|
|
157
|
+
# Generate random name, if no name was supplied
|
|
158
|
+
if name is None:
|
|
159
|
+
name = "func_" + "".join(
|
|
160
|
+
[random.choice(string.ascii_letters) for i in range(8)]
|
|
161
|
+
)
|
|
162
|
+
arg_strs = list()
|
|
163
|
+
for arg in args:
|
|
164
|
+
if arg.islower():
|
|
165
|
+
arg_str = f"double {arg}"
|
|
166
|
+
elif arg.isupper():
|
|
167
|
+
arg_str = f"double {arg}[3]"
|
|
168
|
+
else:
|
|
169
|
+
raise Exception
|
|
170
|
+
arg_strs.append(arg_str)
|
|
171
|
+
args_str = ", ".join(arg_strs)
|
|
172
|
+
|
|
173
|
+
# This allows using the 'boys' function without producing an error
|
|
174
|
+
print_settings = {
|
|
175
|
+
"allow_unknown_functions": True,
|
|
176
|
+
# Without disabling contract some expressions will raise ValueError.
|
|
177
|
+
"contract": False,
|
|
178
|
+
}
|
|
179
|
+
print_func = C99CodePrinter(print_settings).doprint
|
|
180
|
+
assignments = [Assignment(lhs, rhs) for lhs, rhs in repls]
|
|
181
|
+
repl_lines = [print_func(as_) for as_ in assignments]
|
|
182
|
+
res_lines = [print_func(red) for red in reduced]
|
|
183
|
+
res_len = len(reduced)
|
|
184
|
+
|
|
185
|
+
signature = f"double * {name}({args_str})"
|
|
186
|
+
|
|
187
|
+
tpl = Template(
|
|
188
|
+
"""
|
|
189
|
+
{{ signature }} {
|
|
190
|
+
{% if doc_str %}
|
|
191
|
+
/* {{ doc_str }} */
|
|
192
|
+
{% endif %}
|
|
193
|
+
|
|
194
|
+
static double {{ res_name }}[{{ res_len}}];
|
|
195
|
+
|
|
196
|
+
{% for line in repl_lines %}
|
|
197
|
+
const double {{ line }}
|
|
198
|
+
{% endfor %}
|
|
199
|
+
|
|
200
|
+
{% for rhs in res_lines %}
|
|
201
|
+
{{ res_name }}[{{ loop.index0}}] = {{ rhs }};
|
|
202
|
+
{% endfor %}
|
|
203
|
+
|
|
204
|
+
return {{ res_name }};
|
|
205
|
+
}
|
|
206
|
+
""",
|
|
207
|
+
trim_blocks=True,
|
|
208
|
+
lstrip_blocks=True,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
rendered = textwrap.dedent(
|
|
212
|
+
tpl.render(
|
|
213
|
+
signature=signature,
|
|
214
|
+
res_name="results",
|
|
215
|
+
res_len=res_len,
|
|
216
|
+
repl_lines=repl_lines,
|
|
217
|
+
res_lines=res_lines, # c_lines=c_lines,
|
|
218
|
+
reduced=reduced,
|
|
219
|
+
doc_str=doc_str,
|
|
220
|
+
args_str=args_str,
|
|
221
|
+
)
|
|
222
|
+
).strip()
|
|
223
|
+
return rendered, signature
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def canonical_order(L):
|
|
227
|
+
inds = list()
|
|
228
|
+
for i in range(L + 1):
|
|
229
|
+
l = L - i
|
|
230
|
+
for n in range(i + 1):
|
|
231
|
+
m = i - n
|
|
232
|
+
inds.append((l, m, n))
|
|
233
|
+
return inds
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def shell_iter(Ls):
|
|
237
|
+
"""Iterator over cartesian product of L values in Ls."""
|
|
238
|
+
return it.product(*[canonical_order(L) for L in Ls])
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class CartGTO3d(Function):
|
|
242
|
+
"""3D Cartesian Gaussian function; not normalized."""
|
|
243
|
+
|
|
244
|
+
@classmethod
|
|
245
|
+
@functools.cache
|
|
246
|
+
def eval(cls, i, j, k, a, Xa, Ya, Za):
|
|
247
|
+
Xa2 = Xa**2
|
|
248
|
+
Ya2 = Ya**2
|
|
249
|
+
Za2 = Za**2
|
|
250
|
+
return (Xa**i) * (Ya**j) * (Za**k) * exp(-a * (Xa2 + Ya2 + Za2))
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class CartGTOShell(Function):
|
|
254
|
+
@classmethod
|
|
255
|
+
def eval(cls, La_tot, a, Xa, Ya, Za):
|
|
256
|
+
exprs = [CartGTO3d(*La, a, Xa, Ya, Za) for La, in shell_iter((La_tot,))]
|
|
257
|
+
# print(CartGTO3d.eval.cache_info())
|
|
258
|
+
return exprs
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class TwoCenter1d(Expr):
|
|
262
|
+
def __new__(self, a, A, b, B, C=None):
|
|
263
|
+
self.a = a
|
|
264
|
+
self.A = A
|
|
265
|
+
self.b = b
|
|
266
|
+
self.B = B
|
|
267
|
+
self.C = C
|
|
268
|
+
return super().__new__(self, a, A, b, B, C)
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def p(self):
|
|
272
|
+
"""Total exponent p."""
|
|
273
|
+
return self.a + self.b
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def mu(self):
|
|
277
|
+
"""Reduced exponent mu."""
|
|
278
|
+
return self.a * self.b / self.p
|
|
279
|
+
|
|
280
|
+
@property
|
|
281
|
+
def AB(self):
|
|
282
|
+
"""Relative coordinate/Gaussian separation X_AB."""
|
|
283
|
+
return self.A - self.B
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def P(self):
|
|
287
|
+
"""Center-of-charge coordinate."""
|
|
288
|
+
return (self.a * self.A + self.b * self.B) / self.p
|
|
289
|
+
|
|
290
|
+
@property
|
|
291
|
+
def PA(self):
|
|
292
|
+
"""Relative coordinate/Gaussian separation X_PA."""
|
|
293
|
+
return self.P - self.A
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def PB(self):
|
|
297
|
+
"""Relative coordinate/Gaussian separation X_PB."""
|
|
298
|
+
return self.P - self.B
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def PC(self):
|
|
302
|
+
"""Relative coordinate/Gaussian separation X_PC."""
|
|
303
|
+
return self.P - self.C
|
|
304
|
+
|
|
305
|
+
@property
|
|
306
|
+
def K(self):
|
|
307
|
+
return exp(-self.mu * self.AB**2)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class Multipole1d(TwoCenter1d):
|
|
311
|
+
"""1d multipole-moment integral of order 'e', between primitive 1d Gaussians
|
|
312
|
+
Ga = G_i(a, r, A) and Gb = G_j(b, r, B) with Cartesian quantum number i and j,
|
|
313
|
+
exponents a and b, centered at A (B). The origin of the multipole expansion is
|
|
314
|
+
at C.
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
@functools.cache
|
|
318
|
+
def eval(self, i, j, e):
|
|
319
|
+
ang_moms = (i, j, e)
|
|
320
|
+
if any([_ < 0 for _ in ang_moms]):
|
|
321
|
+
return 0
|
|
322
|
+
|
|
323
|
+
recur = self.eval
|
|
324
|
+
|
|
325
|
+
def vrr(i, j, e, X):
|
|
326
|
+
return X * recur(i, j, e) + 1 / (2 * self.p) * (
|
|
327
|
+
i * recur(i - 1, j, e) + j * recur(i, j - 1, e) + e * recur(i, j, e - 1)
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# Base case
|
|
331
|
+
if all([_ == 0 for _ in ang_moms]):
|
|
332
|
+
return sqrt(pi / self.p) * self.K
|
|
333
|
+
# Decrement i
|
|
334
|
+
elif i.is_positive:
|
|
335
|
+
return vrr(i - 1, j, e, self.PA)
|
|
336
|
+
# Decrement j
|
|
337
|
+
elif j.is_positive:
|
|
338
|
+
return vrr(i, j - 1, e, self.PB)
|
|
339
|
+
elif e.is_positive:
|
|
340
|
+
return vrr(i, j, e - 1, self.PC)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class Multipole3d(Function):
|
|
344
|
+
@classmethod
|
|
345
|
+
def eval(cls, La, Lb, a, b, A, B, Le, C):
|
|
346
|
+
x, y, z = [
|
|
347
|
+
Multipole1d(a, A[i], b, B[i], C[i]).eval(La[i], Lb[i], Le[i])
|
|
348
|
+
for i in range(3)
|
|
349
|
+
]
|
|
350
|
+
return x * y * z
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class Multipole3dShell(Function):
|
|
354
|
+
@classmethod
|
|
355
|
+
def eval(cls, La_tot, Lb_tot, a, b, A, B, Le_tot=0, C=(0.0, 0.0, 0.0)):
|
|
356
|
+
exprs = [
|
|
357
|
+
Multipole3d(La, Lb, a, b, A, B, Le, C)
|
|
358
|
+
for Le, La, Lb in shell_iter((Le_tot, La_tot, Lb_tot))
|
|
359
|
+
]
|
|
360
|
+
# print(Multipole1d.eval.cache_info())
|
|
361
|
+
return exprs
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class DiagQuadrupole3dShell(Function):
|
|
365
|
+
@classmethod
|
|
366
|
+
def eval(cls, La_tot, Lb_tot, a, b, A, B, C=(0.0, 0.0, 0.0)):
|
|
367
|
+
exprs = list()
|
|
368
|
+
for Le in ((2, 0, 0), (0, 2, 0), (0, 0, 2)):
|
|
369
|
+
for La, Lb in shell_iter((La_tot, Lb_tot)):
|
|
370
|
+
exprs.append(Multipole3d(La, Lb, a, b, A, B, Le, C))
|
|
371
|
+
# print(DiagQuadrupole3dShell.eval.cache_info())
|
|
372
|
+
return exprs
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class Overlap1d(TwoCenter1d):
|
|
376
|
+
def eval(self, i, j):
|
|
377
|
+
return Multipole1d(self.a, self.A, self.b, self.B).eval(i, j, 0)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
class Overlap3dShell(Function):
|
|
381
|
+
"""Whole shell of 3d overlap integrals for a given L pair
|
|
382
|
+
over primitive gaussians."""
|
|
383
|
+
|
|
384
|
+
@classmethod
|
|
385
|
+
def eval(cls, La_tot, Lb_tot, a, b, A, B):
|
|
386
|
+
exprs = Multipole3dShell(La_tot, Lb_tot, a, b, A, B)
|
|
387
|
+
# print(Multipole1d.eval.cache_info())
|
|
388
|
+
return exprs
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class Kinetic1d(TwoCenter1d):
|
|
392
|
+
@functools.cache
|
|
393
|
+
def eval(self, i, j):
|
|
394
|
+
if i < 0 or j < 0:
|
|
395
|
+
return 0
|
|
396
|
+
|
|
397
|
+
recur = self.eval
|
|
398
|
+
|
|
399
|
+
def recur_rel(i, j, X):
|
|
400
|
+
return X * recur(i, j) + 1 / (2 * self.p) * (
|
|
401
|
+
i * recur(i - 1, j) + j * recur(i, j - 1)
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
def recur_ovlp(i, j):
|
|
405
|
+
return Overlap1d(self.a, self.A, self.b, self.B).eval(i, j)
|
|
406
|
+
|
|
407
|
+
# Base case
|
|
408
|
+
if i == 0 and j == 0:
|
|
409
|
+
return (
|
|
410
|
+
self.a - 2 * self.a**2 * (self.PA**2 + 1 / (2 * self.p))
|
|
411
|
+
) * recur_ovlp(i, j)
|
|
412
|
+
# Decrement i
|
|
413
|
+
elif i > 0:
|
|
414
|
+
# Eq. (9.3.41)
|
|
415
|
+
return recur_rel(i - 1, j, self.PA) + self.b / self.p * (
|
|
416
|
+
2 * self.a * recur_ovlp(i, j) - i * recur_ovlp(i - 2, j)
|
|
417
|
+
)
|
|
418
|
+
# Decrement j
|
|
419
|
+
elif j > 0:
|
|
420
|
+
# Eq. (9.3.41)
|
|
421
|
+
return recur_rel(i, j - 1, self.PB) + self.a / self.p * (
|
|
422
|
+
2 * self.b * recur_ovlp(i, j) - j * recur_ovlp(i, j - 2)
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class Kinetic3d(Function):
|
|
427
|
+
@classmethod
|
|
428
|
+
def eval(cls, La, Lb, a, b, A, B):
|
|
429
|
+
Tx, Ty, Tz = [Kinetic1d(a, A[i], b, B[i]).eval(La[i], Lb[i]) for i in range(3)]
|
|
430
|
+
Sx, Sy, Sz = [Overlap1d(a, A[i], b, B[i]).eval(La[i], Lb[i]) for i in range(3)]
|
|
431
|
+
return Tx * Sy * Sz + Sx * Ty * Sz + Sx * Sy * Tz
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
class Kinetic3dShell(Function):
|
|
435
|
+
@classmethod
|
|
436
|
+
def eval(cls, La_tot, Lb_tot, a, b, A, B):
|
|
437
|
+
exprs = [
|
|
438
|
+
Kinetic3d(La, Lb, a, b, A, B) for La, Lb in shell_iter((La_tot, Lb_tot))
|
|
439
|
+
]
|
|
440
|
+
return exprs
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# Placeholder for the Boys-function. The actual Boys-function will be
|
|
444
|
+
# imported in the generated python module.
|
|
445
|
+
boys = Function("boys")
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
class Coulomb(TwoCenter1d):
|
|
449
|
+
"""Nucleus at C."""
|
|
450
|
+
|
|
451
|
+
@functools.cache
|
|
452
|
+
def eval(self, i, k, m, j, l, n, N):
|
|
453
|
+
ang_moms = (i, k, m, j, l, n)
|
|
454
|
+
if any([am < 0 for am in ang_moms]):
|
|
455
|
+
return 0
|
|
456
|
+
|
|
457
|
+
def recur(N, *inds):
|
|
458
|
+
"""Simple wrapper to pass all required arguments."""
|
|
459
|
+
return self.eval(*inds, N) # , a, b, A, B, C)
|
|
460
|
+
|
|
461
|
+
def decr(to_decr, decr_ind):
|
|
462
|
+
one = np.zeros(3, dtype=int)
|
|
463
|
+
one[decr_ind] = 1
|
|
464
|
+
|
|
465
|
+
def Ni(inds):
|
|
466
|
+
return inds[decr_ind]
|
|
467
|
+
|
|
468
|
+
bra = np.array((i, k, m), dtype=int)
|
|
469
|
+
ket = np.array((j, l, n), dtype=int)
|
|
470
|
+
|
|
471
|
+
if to_decr == "bra":
|
|
472
|
+
X = self.PA
|
|
473
|
+
bra[decr_ind] -= 1
|
|
474
|
+
else:
|
|
475
|
+
X = self.PB
|
|
476
|
+
ket[decr_ind] -= 1
|
|
477
|
+
|
|
478
|
+
bra_decr = bra - one
|
|
479
|
+
ket_decr = ket - one
|
|
480
|
+
|
|
481
|
+
return (
|
|
482
|
+
X[decr_ind] * recur(N, *bra, *ket)
|
|
483
|
+
- self.PC[decr_ind] * recur(N + 1, *bra, *ket)
|
|
484
|
+
+ 1
|
|
485
|
+
/ (2 * self.p)
|
|
486
|
+
* Ni(bra)
|
|
487
|
+
* (recur(N, *bra_decr, *ket) - recur(N + 1, *bra_decr, *ket))
|
|
488
|
+
+ 1
|
|
489
|
+
/ (2 * self.p)
|
|
490
|
+
* Ni(ket)
|
|
491
|
+
* (recur(N, *bra, *ket_decr) - recur(N + 1, *bra, *ket_decr))
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# Base case
|
|
495
|
+
if all([am == 0 for am in ang_moms]):
|
|
496
|
+
K = exp(-self.mu * self.AB.dot(self.AB))
|
|
497
|
+
return 2 * pi / self.p * K * boys(N, self.p * self.PC.dot(self.PC))
|
|
498
|
+
elif i > 0:
|
|
499
|
+
return decr("bra", 0)
|
|
500
|
+
elif j > 0:
|
|
501
|
+
return decr("ket", 0)
|
|
502
|
+
elif k > 0:
|
|
503
|
+
return decr("bra", 1)
|
|
504
|
+
elif l > 0:
|
|
505
|
+
return decr("ket", 1)
|
|
506
|
+
elif m > 0:
|
|
507
|
+
return decr("bra", 2)
|
|
508
|
+
elif n > 0:
|
|
509
|
+
return decr("ket", 2)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
class CoulombShell(Function):
|
|
513
|
+
@classmethod
|
|
514
|
+
def eval(cls, La_tot, Lb_tot, a, b, A, B, C=(0.0, 0.0, 0.0)):
|
|
515
|
+
exprs = [
|
|
516
|
+
# Coulomb(*La, *Lb, 0, a, b, A, B, C)
|
|
517
|
+
Coulomb(a, A, b, B, C).eval(*La, *Lb, 0)
|
|
518
|
+
for La, Lb in shell_iter((La_tot, Lb_tot))
|
|
519
|
+
]
|
|
520
|
+
# print(Coulomb.eval.cache_info())
|
|
521
|
+
return exprs
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
class TwoCenterTwoElectron(Function):
|
|
525
|
+
@classmethod
|
|
526
|
+
@functools.cache
|
|
527
|
+
def eval(cls, ia, ja, ka, ib, jb, kb, N, a, b, A, B):
|
|
528
|
+
ang_moms = np.array((ia, ja, ka, ib, jb, kb), dtype=int)
|
|
529
|
+
ang_moms2d = ang_moms.reshape(-1, 3)
|
|
530
|
+
if any([am < 0 for am in ang_moms]):
|
|
531
|
+
return 0
|
|
532
|
+
|
|
533
|
+
p = a + b
|
|
534
|
+
P = (a * A + b * B) / p
|
|
535
|
+
mu = (a * b) / p
|
|
536
|
+
|
|
537
|
+
def recur(N, *inds):
|
|
538
|
+
return cls(*inds, N, a, b, A, B)
|
|
539
|
+
|
|
540
|
+
def vrr(bra_or_ket, cart_ind):
|
|
541
|
+
assert bra_or_ket in ("bra", "ket")
|
|
542
|
+
|
|
543
|
+
if bra_or_ket == "bra":
|
|
544
|
+
ind1 = 0
|
|
545
|
+
ind2 = 1
|
|
546
|
+
else:
|
|
547
|
+
ind1 = 1
|
|
548
|
+
ind2 = 0
|
|
549
|
+
exps = (a, b)
|
|
550
|
+
X = (P - A, P - B)[ind1][cart_ind]
|
|
551
|
+
|
|
552
|
+
l1 = ang_moms2d[ind1, cart_ind] - 1
|
|
553
|
+
exp1 = exps[ind1]
|
|
554
|
+
decr1 = ang_moms2d.copy()
|
|
555
|
+
decr1[ind1, cart_ind] -= 1
|
|
556
|
+
decr11 = decr1.copy()
|
|
557
|
+
decr11[ind1, cart_ind] -= 1
|
|
558
|
+
|
|
559
|
+
l2 = ang_moms2d[ind2, cart_ind]
|
|
560
|
+
exp2 = exps[ind2]
|
|
561
|
+
decr12 = ang_moms2d.copy()
|
|
562
|
+
decr12[ind1, cart_ind] -= 1
|
|
563
|
+
decr12[ind2, cart_ind] -= 1
|
|
564
|
+
|
|
565
|
+
decr1 = decr1.flatten()
|
|
566
|
+
decr11 = decr11.flatten()
|
|
567
|
+
decr12 = decr12.flatten()
|
|
568
|
+
|
|
569
|
+
return (
|
|
570
|
+
X * recur(N + 1, *decr1)
|
|
571
|
+
+ l1
|
|
572
|
+
/ (2 * exp1)
|
|
573
|
+
* (recur(N, *decr11) - exp2 / p * recur(N + 1, *decr11))
|
|
574
|
+
+ l2 / (2 * p) * recur(N + 1, *decr12)
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
# vrr_bra = functools.partial(vrr, bra_or_ket="bra")
|
|
578
|
+
vrr_bra = functools.partial(vrr, "bra")
|
|
579
|
+
vrr_ket = functools.partial(vrr, "ket")
|
|
580
|
+
|
|
581
|
+
# Base case
|
|
582
|
+
if (ang_moms == 0).all():
|
|
583
|
+
AB = A - B
|
|
584
|
+
return 2 * pi**2.5 / sqrt(p) / (a * b) * boys(N, mu * AB.dot(AB))
|
|
585
|
+
elif ia > 0:
|
|
586
|
+
return vrr_bra(0)
|
|
587
|
+
elif ja > 0:
|
|
588
|
+
return vrr_bra(1)
|
|
589
|
+
elif ka > 0:
|
|
590
|
+
return vrr_bra(2)
|
|
591
|
+
elif ib > 0:
|
|
592
|
+
return vrr_ket(0)
|
|
593
|
+
elif jb > 0:
|
|
594
|
+
return vrr_ket(1)
|
|
595
|
+
elif kb > 0:
|
|
596
|
+
return vrr_ket(2)
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class TwoCenterTwoElectronShell(Function):
|
|
600
|
+
@classmethod
|
|
601
|
+
def eval(cls, La_tot, Lb_tot, a, b, A, B):
|
|
602
|
+
exprs = [
|
|
603
|
+
TwoCenterTwoElectron(*La, *Lb, 0, a, b, A, B)
|
|
604
|
+
for La, Lb in shell_iter((La_tot, Lb_tot))
|
|
605
|
+
]
|
|
606
|
+
# print(TwoCenterTwoElectron.eval.cache_info())
|
|
607
|
+
return exprs
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
class ThreeCenterTwoElectronBase(Function):
|
|
611
|
+
"""
|
|
612
|
+
https://pubs.rsc.org/en/content/articlelanding/2004/CP/b413539c
|
|
613
|
+
|
|
614
|
+
There is an error in the base case (00|0). One must divide by
|
|
615
|
+
sqrt(eta + gamma), not multiply.
|
|
616
|
+
"""
|
|
617
|
+
|
|
618
|
+
@classmethod
|
|
619
|
+
@functools.cache
|
|
620
|
+
def eval(cls, ia, ja, ka, ib, jb, kb, ic, jc, kc, N, a, b, c, A, B, C):
|
|
621
|
+
ang_moms = np.array((ia, ja, ka, ib, jb, kb, ic, jc, kc), dtype=int)
|
|
622
|
+
ang_moms2d = ang_moms.reshape(-1, 3)
|
|
623
|
+
if any([am < 0 for am in ang_moms]):
|
|
624
|
+
return 0
|
|
625
|
+
|
|
626
|
+
p = a + b
|
|
627
|
+
P = (a * A + b * B) / p
|
|
628
|
+
mu = (a * b) / p
|
|
629
|
+
X_PC = P - C
|
|
630
|
+
|
|
631
|
+
rho = p * c / (p + c)
|
|
632
|
+
|
|
633
|
+
def recur(N, *inds):
|
|
634
|
+
"""Simple wrapper to pass all required arguments.
|
|
635
|
+
|
|
636
|
+
Here we don't use ThreeCenterTwoElectronBase, but the derived classes,
|
|
637
|
+
that provide the 'aux_vrr' attribute, to distinguish between the different
|
|
638
|
+
vertical recursion relations."""
|
|
639
|
+
return cls(*inds, N, a, b, c, A, B, C)
|
|
640
|
+
|
|
641
|
+
def recur_hrr(cart_ind):
|
|
642
|
+
"""Horizontal recursion relation to transfer angular momentum in bra.
|
|
643
|
+
|
|
644
|
+
(a, b+1_i|c) = (a + 1_i,b|c) + X_AB (ab|c)
|
|
645
|
+
"""
|
|
646
|
+
assert N == 0
|
|
647
|
+
incr_ang_moms = ang_moms2d.copy()
|
|
648
|
+
incr_ang_moms[0, cart_ind] += 1 # Increase in left orbital
|
|
649
|
+
incr_ang_moms[1, cart_ind] -= 1 # Decrease in right orbital
|
|
650
|
+
|
|
651
|
+
decr_ang_moms = ang_moms2d.copy()
|
|
652
|
+
decr_ang_moms[1, cart_ind] -= 1 # Decrease in right orbital
|
|
653
|
+
|
|
654
|
+
incr_ang_moms = incr_ang_moms.flatten()
|
|
655
|
+
decr_ang_moms = decr_ang_moms.flatten()
|
|
656
|
+
|
|
657
|
+
AB_dir = (A - B)[cart_ind]
|
|
658
|
+
|
|
659
|
+
return recur(N, *incr_ang_moms) + AB_dir * recur(N, *decr_ang_moms)
|
|
660
|
+
|
|
661
|
+
def recur_vrr(cart_ind):
|
|
662
|
+
assert (ib, jb, kb) == (0, 0, 0)
|
|
663
|
+
assert (ic, jc, kc) == (0, 0, 0)
|
|
664
|
+
|
|
665
|
+
decr_a = ang_moms2d.copy()
|
|
666
|
+
decr_a[0, cart_ind] -= 1 # Decrease in bra left orbital
|
|
667
|
+
decr_aa = decr_a.copy()
|
|
668
|
+
decr_aa[0, cart_ind] -= 1 # Decrease in bra left orbital again
|
|
669
|
+
|
|
670
|
+
PA_dir = (P - A)[cart_ind]
|
|
671
|
+
PC_dir = (P - C)[cart_ind]
|
|
672
|
+
ai = (ia, ja, ka)[cart_ind] - 1
|
|
673
|
+
_2p = 2 * p
|
|
674
|
+
|
|
675
|
+
decr_a = decr_a.flatten()
|
|
676
|
+
decr_aa = decr_aa.flatten()
|
|
677
|
+
|
|
678
|
+
return (
|
|
679
|
+
PA_dir * recur(N, *decr_a)
|
|
680
|
+
- rho / p * PC_dir * recur(N + 1, *decr_a)
|
|
681
|
+
+ ai / _2p * (recur(N, *decr_aa) - rho / p * recur(N + 1, *decr_aa))
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
def recur_vrr_aux(cart_ind):
|
|
685
|
+
decr_c = ang_moms2d.copy()
|
|
686
|
+
decr_c[2, cart_ind] -= 1
|
|
687
|
+
|
|
688
|
+
decr_cc = decr_c.copy()
|
|
689
|
+
decr_cc[2, cart_ind] -= 1
|
|
690
|
+
|
|
691
|
+
decr_ac = decr_c.copy()
|
|
692
|
+
decr_ac[0, cart_ind] -= 1
|
|
693
|
+
|
|
694
|
+
decr_bc = decr_c.copy()
|
|
695
|
+
decr_bc[1, cart_ind] -= 1
|
|
696
|
+
|
|
697
|
+
PC_dir = (P - C)[cart_ind]
|
|
698
|
+
la = (ia, ja, ka)[cart_ind]
|
|
699
|
+
lb = (ib, jb, kb)[cart_ind]
|
|
700
|
+
lc = (ic, jc, kc)[cart_ind] - 1
|
|
701
|
+
|
|
702
|
+
decr_c = decr_c.flatten()
|
|
703
|
+
decr_cc = decr_cc.flatten()
|
|
704
|
+
decr_ac = decr_ac.flatten()
|
|
705
|
+
decr_bc = decr_bc.flatten()
|
|
706
|
+
|
|
707
|
+
return (
|
|
708
|
+
p / (p + c) * PC_dir * recur(N + 1, *decr_c)
|
|
709
|
+
+ lc
|
|
710
|
+
/ (2 * c)
|
|
711
|
+
* (recur(N, *decr_cc) - p / (p + c) * recur(N + 1, *decr_cc))
|
|
712
|
+
+ la / (2 * (p + c)) * recur(N + 1, *decr_ac)
|
|
713
|
+
+ lb / (2 * (p + c)) * recur(N + 1, *decr_bc)
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
def recur_vrr_aux_sph(cart_ind):
|
|
717
|
+
assert (ib, jb, kb) == (0, 0, 0)
|
|
718
|
+
decr_c = ang_moms2d.copy()
|
|
719
|
+
decr_c[2, cart_ind] -= 1
|
|
720
|
+
decr_ac = decr_c.copy()
|
|
721
|
+
decr_ac[0, cart_ind] -= 1
|
|
722
|
+
La = (ia, ja, ka)[cart_ind]
|
|
723
|
+
PC_dir = (P - C)[cart_ind]
|
|
724
|
+
return (
|
|
725
|
+
rho
|
|
726
|
+
/ c
|
|
727
|
+
* (
|
|
728
|
+
PC_dir * recur(N + 1, *decr_c.flatten())
|
|
729
|
+
+ La / (2 * p) * recur(N + 1, *decr_ac.flatten())
|
|
730
|
+
)
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
recur_vrr_aux_funcs = {
|
|
734
|
+
"cart": recur_vrr_aux,
|
|
735
|
+
"sph": recur_vrr_aux_sph,
|
|
736
|
+
}
|
|
737
|
+
recur_vrr_aux_func = recur_vrr_aux_funcs[cls.aux_vrr]
|
|
738
|
+
|
|
739
|
+
# Base case
|
|
740
|
+
if (ang_moms == 0).all():
|
|
741
|
+
X_AB = A - B
|
|
742
|
+
r2_PC = X_PC.dot(X_PC)
|
|
743
|
+
r2_AB = X_AB.dot(X_AB)
|
|
744
|
+
chi = rho * r2_PC
|
|
745
|
+
K = exp(-mu * r2_AB)
|
|
746
|
+
return 2 * pi**2.5 / sqrt(p + c) / (p * c) * K * boys(N, chi)
|
|
747
|
+
elif ib > 0:
|
|
748
|
+
return recur_hrr(0)
|
|
749
|
+
elif jb > 0:
|
|
750
|
+
return recur_hrr(1)
|
|
751
|
+
elif kb > 0:
|
|
752
|
+
return recur_hrr(2)
|
|
753
|
+
elif ic > 0:
|
|
754
|
+
return recur_vrr_aux_func(0)
|
|
755
|
+
elif jc > 0:
|
|
756
|
+
return recur_vrr_aux_func(1)
|
|
757
|
+
elif kc > 0:
|
|
758
|
+
return recur_vrr_aux_func(2)
|
|
759
|
+
elif ia > 0:
|
|
760
|
+
return recur_vrr(0)
|
|
761
|
+
elif ja > 0:
|
|
762
|
+
return recur_vrr(1)
|
|
763
|
+
elif ka > 0:
|
|
764
|
+
return recur_vrr(2)
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
class ThreeCenterTwoElectron(ThreeCenterTwoElectronBase):
|
|
768
|
+
aux_vrr = "cart"
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
class ThreeCenterTwoElectronSph(ThreeCenterTwoElectronBase):
|
|
772
|
+
aux_vrr = "sph"
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
class ThreeCenterTwoElectronShell(Function):
|
|
776
|
+
@classmethod
|
|
777
|
+
def eval(cls, La_tot, Lb_tot, Lc_tot, a, b, c, A, B, C):
|
|
778
|
+
exprs = [
|
|
779
|
+
ThreeCenterTwoElectron(*La, *Lb, *Lc, 0, a, b, c, A, B, C)
|
|
780
|
+
for La, Lb, Lc in shell_iter((La_tot, Lb_tot, Lc_tot))
|
|
781
|
+
]
|
|
782
|
+
# print(ThreeCenterTwoElectron.eval.cache_info())
|
|
783
|
+
return exprs
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
class ThreeCenterTwoElectronSphShell(Function):
|
|
787
|
+
@classmethod
|
|
788
|
+
def eval(cls, La_tot, Lb_tot, Lc_tot, a, b, c, A, B, C):
|
|
789
|
+
exprs = [
|
|
790
|
+
ThreeCenterTwoElectronSph(*La, *Lb, *Lc, 0, a, b, c, A, B, C)
|
|
791
|
+
for La, Lb, Lc in shell_iter((La_tot, Lb_tot, Lc_tot))
|
|
792
|
+
]
|
|
793
|
+
# print(ThreeCenterTwoElectron.eval.cache_info())
|
|
794
|
+
return exprs
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
class ERI(Function):
|
|
798
|
+
"""Variables named according to libreta paper [3]."""
|
|
799
|
+
|
|
800
|
+
@classmethod
|
|
801
|
+
def eval(
|
|
802
|
+
cls, ia, ja, ka, ib, jb, kb, ic, jc, kc, id_, jd, kd, N, a, b, c, d, A, B, C, D
|
|
803
|
+
):
|
|
804
|
+
ang_moms = np.array(
|
|
805
|
+
(ia, ja, ka, ib, jb, kb, ic, jc, kc, id_, jd, kd), dtype=int
|
|
806
|
+
)
|
|
807
|
+
ang_moms2d = ang_moms.reshape(-1, 3)
|
|
808
|
+
|
|
809
|
+
if any([am < 0 for am in ang_moms]):
|
|
810
|
+
return 0
|
|
811
|
+
|
|
812
|
+
AB = A - B
|
|
813
|
+
xi = a + b
|
|
814
|
+
P = (a * A + b * B) / xi # Eq. (5)
|
|
815
|
+
|
|
816
|
+
CD = C - D
|
|
817
|
+
zeta = c + d
|
|
818
|
+
Q = (c * C + d * D) / zeta # Eq. (6)
|
|
819
|
+
|
|
820
|
+
theta = xi * zeta / (xi + zeta) # Eq. (7)
|
|
821
|
+
|
|
822
|
+
def recur(N, *ang_moms):
|
|
823
|
+
"""Simple wrapper to pass all required arguments."""
|
|
824
|
+
return ERI(*ang_moms, N, a, b, c, d, A, B, C, D)
|
|
825
|
+
|
|
826
|
+
def recur_hrr(bra_or_ket, cart_ind):
|
|
827
|
+
"""Horizontal recursion relation to transfer angmoms in bra or ket.
|
|
828
|
+
|
|
829
|
+
(a, b+1_x|cd) = (a + 1_x,b|cd) + X_AB (ab|cd)
|
|
830
|
+
(ab|c, d + 1_x) = (ab|c + 1_x, d) + X_CD (ab|cd)
|
|
831
|
+
"""
|
|
832
|
+
if bra_or_ket == "bra":
|
|
833
|
+
XYZ = AB
|
|
834
|
+
incr_ind = 0
|
|
835
|
+
else:
|
|
836
|
+
XYZ = CD
|
|
837
|
+
incr_ind = 2
|
|
838
|
+
|
|
839
|
+
decr_ind = incr_ind + 1
|
|
840
|
+
incr_ang_moms = ang_moms2d.copy()
|
|
841
|
+
incr_ang_moms[incr_ind, cart_ind] += 1 # Increase in left orbital
|
|
842
|
+
incr_ang_moms[decr_ind, cart_ind] -= 1 # Decrease in right orbital
|
|
843
|
+
incr_ang_moms = incr_ang_moms.flatten()
|
|
844
|
+
decr_ang_moms = ang_moms2d.copy()
|
|
845
|
+
decr_ang_moms[decr_ind, cart_ind] -= 1
|
|
846
|
+
decr_ang_moms = decr_ang_moms.flatten()
|
|
847
|
+
|
|
848
|
+
return recur(N, *incr_ang_moms) + XYZ[cart_ind] * recur(N, *decr_ang_moms)
|
|
849
|
+
|
|
850
|
+
def recur_vrr(bra_or_ket, cart_ind):
|
|
851
|
+
assert (ib, jb, kb) == (0, 0, 0)
|
|
852
|
+
assert (id_, jd, kd) == (0, 0, 0)
|
|
853
|
+
|
|
854
|
+
if bra_or_ket == "bra":
|
|
855
|
+
XYZ = P - A
|
|
856
|
+
decr_ind = 0
|
|
857
|
+
decr_also_ind = 2
|
|
858
|
+
exp1, exp2 = zeta, xi
|
|
859
|
+
am1 = (ia, ja, ka)[cart_ind] - 1
|
|
860
|
+
am2 = (ic, jc, kc)[cart_ind]
|
|
861
|
+
sign = -1
|
|
862
|
+
else:
|
|
863
|
+
XYZ = Q - C
|
|
864
|
+
decr_ind = 2
|
|
865
|
+
decr_also_ind = 0
|
|
866
|
+
exp1, exp2 = xi, zeta
|
|
867
|
+
am1 = (ic, jc, kc)[cart_ind] - 1
|
|
868
|
+
am2 = (ia, ja, ka)[cart_ind]
|
|
869
|
+
sign = 1
|
|
870
|
+
|
|
871
|
+
decr_ang_moms = ang_moms2d.copy()
|
|
872
|
+
decr_ang_moms[decr_ind, cart_ind] -= 1
|
|
873
|
+
decr_ang_moms = decr_ang_moms.flatten()
|
|
874
|
+
decr2_ang_moms = ang_moms2d.copy()
|
|
875
|
+
decr2_ang_moms[decr_ind, cart_ind] -= 2 # Further decrease angular momentum
|
|
876
|
+
decr2_ang_moms = decr2_ang_moms.flatten()
|
|
877
|
+
decr_also_ang_moms = ang_moms2d.copy()
|
|
878
|
+
decr_also_ang_moms[decr_also_ind, cart_ind] -= 1
|
|
879
|
+
decr_also_ang_moms = decr_also_ang_moms.flatten()
|
|
880
|
+
|
|
881
|
+
denom = xi + zeta
|
|
882
|
+
quot = exp1 / denom
|
|
883
|
+
PQ = P - Q
|
|
884
|
+
|
|
885
|
+
return (
|
|
886
|
+
XYZ[cart_ind] * recur(N, *decr_ang_moms)
|
|
887
|
+
+ sign * quot * PQ[cart_ind] * recur(N + 1, *decr_ang_moms)
|
|
888
|
+
+ am1
|
|
889
|
+
/ (2 * exp2)
|
|
890
|
+
* (recur(N, *decr2_ang_moms) - quot * recur(N + 1, *decr2_ang_moms))
|
|
891
|
+
+ am2 / (2 * denom) * recur(N + 1, *decr_also_ang_moms)
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
# Base case
|
|
895
|
+
if all([am == 0 for am in ang_moms]):
|
|
896
|
+
RAB2 = AB.dot(AB)
|
|
897
|
+
RCD2 = CD.dot(CD)
|
|
898
|
+
Kab = exp(-(a * b) / xi * RAB2)
|
|
899
|
+
Kcd = exp(-(c * d) / zeta * RCD2)
|
|
900
|
+
|
|
901
|
+
PQ = P - Q
|
|
902
|
+
RPQ2 = PQ.dot(PQ)
|
|
903
|
+
|
|
904
|
+
return (
|
|
905
|
+
2
|
|
906
|
+
* pi ** (5 / 2)
|
|
907
|
+
/ (xi * zeta + sqrt(xi + zeta))
|
|
908
|
+
* Kab
|
|
909
|
+
* Kcd
|
|
910
|
+
* boys(N, theta * RPQ2)
|
|
911
|
+
) # Eq. (8) [ss|ss]^N
|
|
912
|
+
#
|
|
913
|
+
# Horizontal recursion relation to reduce angular momentum.
|
|
914
|
+
#
|
|
915
|
+
# Bra, basis func. with exp. b, centered at B with Lb_tot = (ib + jb + kb)
|
|
916
|
+
elif ib > 0:
|
|
917
|
+
return recur_hrr("bra", 0)
|
|
918
|
+
elif jb > 0:
|
|
919
|
+
return recur_hrr("bra", 1)
|
|
920
|
+
elif kb > 0:
|
|
921
|
+
return recur_hrr("bra", 2)
|
|
922
|
+
# Ket, basis func. with exp. d, centered at D with Ld_tot = (id_ + jd + kd)
|
|
923
|
+
elif id_ > 0:
|
|
924
|
+
return recur_hrr("ket", 0)
|
|
925
|
+
elif jd > 0:
|
|
926
|
+
return recur_hrr("ket", 1)
|
|
927
|
+
elif kd > 0:
|
|
928
|
+
return recur_hrr("ket", 2)
|
|
929
|
+
#
|
|
930
|
+
# Vertical recursion relation to reduce angular momentum.
|
|
931
|
+
#
|
|
932
|
+
# Bra, basis func. with exp. a, centered at A with La_tot = (ia + ja + ka)
|
|
933
|
+
elif ia > 0:
|
|
934
|
+
return recur_vrr("bra", 0)
|
|
935
|
+
elif ja > 0:
|
|
936
|
+
return recur_vrr("bra", 1)
|
|
937
|
+
elif ka > 0:
|
|
938
|
+
return recur_vrr("bra", 2)
|
|
939
|
+
# Ket, basis func. with exp. c, centered at C with Lc_tot = (ic + jc + kc)
|
|
940
|
+
elif ic > 0:
|
|
941
|
+
return recur_vrr("ket", 0)
|
|
942
|
+
elif jc > 0:
|
|
943
|
+
return recur_vrr("ket", 1)
|
|
944
|
+
elif kc > 0:
|
|
945
|
+
return recur_vrr("ket", 1)
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
class ERIShell(Function):
|
|
949
|
+
@classmethod
|
|
950
|
+
def eval(cls, La_tot, Lb_tot, Lc_tot, Ld_tot, a, b, c, d, A, B, C, D):
|
|
951
|
+
exprs = [
|
|
952
|
+
ERI(*La, *Lb, *Lc, *Ld, 0, a, b, c, d, A, B, C, D)
|
|
953
|
+
for La, Lb, Lc, Ld in shell_iter((La_tot, Lb_tot, Lc_tot, Ld_tot))
|
|
954
|
+
]
|
|
955
|
+
# print(ERI.eval.cache_info())
|
|
956
|
+
return exprs
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
class SpinOrbit(Function):
|
|
960
|
+
"""1-electron spin-orbit interaction integral for Cartesian direction μ."""
|
|
961
|
+
|
|
962
|
+
@classmethod
|
|
963
|
+
@functools.cache
|
|
964
|
+
def eval(cls, i, k, m, j, l, n, N, mu, a, b, A, B, C):
|
|
965
|
+
ang_moms = (i, k, m, j, l, n)
|
|
966
|
+
if any([am < 0 for am in ang_moms]):
|
|
967
|
+
return 0
|
|
968
|
+
|
|
969
|
+
p = a + b
|
|
970
|
+
P = (a * A + b * B) / p
|
|
971
|
+
X_PC = P - C
|
|
972
|
+
|
|
973
|
+
def coulomb(N, *inds):
|
|
974
|
+
return Coulomb(*inds, N, a, b, A, B, C)
|
|
975
|
+
|
|
976
|
+
def recur(N, *inds):
|
|
977
|
+
"""Simple wrapper to pass all required arguments."""
|
|
978
|
+
return SpinOrbit(*inds, N, mu, a, b, A, B, C)
|
|
979
|
+
|
|
980
|
+
def decr(to_decr, decr_ind):
|
|
981
|
+
one = np.zeros(3, dtype=int)
|
|
982
|
+
one[decr_ind] = 1
|
|
983
|
+
|
|
984
|
+
def Ni(inds):
|
|
985
|
+
return inds[decr_ind]
|
|
986
|
+
|
|
987
|
+
bra = np.array((i, k, m), dtype=int)
|
|
988
|
+
ket = np.array((j, l, n), dtype=int)
|
|
989
|
+
|
|
990
|
+
if to_decr == "bra":
|
|
991
|
+
X = P - A
|
|
992
|
+
bra[decr_ind] -= 1
|
|
993
|
+
else:
|
|
994
|
+
X = P - B
|
|
995
|
+
ket[decr_ind] -= 1
|
|
996
|
+
|
|
997
|
+
bra_decr = bra - one
|
|
998
|
+
ket_decr = ket - one
|
|
999
|
+
|
|
1000
|
+
expr = (
|
|
1001
|
+
# The initial part of this recursion is the same as for the 1-electron
|
|
1002
|
+
# Coulomb integrals.
|
|
1003
|
+
X[decr_ind] * recur(N, *bra, *ket)
|
|
1004
|
+
- X_PC[decr_ind] * recur(N + 1, *bra, *ket)
|
|
1005
|
+
+ 1
|
|
1006
|
+
/ (2 * p)
|
|
1007
|
+
* Ni(bra)
|
|
1008
|
+
* (recur(N, *bra_decr, *ket) - recur(N + 1, *bra_decr, *ket))
|
|
1009
|
+
+ 1
|
|
1010
|
+
/ (2 * p)
|
|
1011
|
+
* Ni(ket)
|
|
1012
|
+
* (recur(N, *bra, *ket_decr) - recur(N + 1, *bra, *ket_decr))
|
|
1013
|
+
# From here on, the recursion differs.
|
|
1014
|
+
)
|
|
1015
|
+
|
|
1016
|
+
if to_decr == "bra":
|
|
1017
|
+
X = B - C
|
|
1018
|
+
exponent = b
|
|
1019
|
+
else:
|
|
1020
|
+
X = A - C
|
|
1021
|
+
exponent = a
|
|
1022
|
+
# Second to line in Eq. (A34) in [1]
|
|
1023
|
+
expr += 2 * exponent * Matrix(one).cross(X)[mu] * coulomb(N + 1, *bra, *ket)
|
|
1024
|
+
# Last line in Eq. (A34) in [1]
|
|
1025
|
+
for k_ in range(
|
|
1026
|
+
3
|
|
1027
|
+
): # Use k_ instead of k, as this is already an angular momentum
|
|
1028
|
+
one_k = np.zeros(3, dtype=int)
|
|
1029
|
+
one_k[k_] = 1
|
|
1030
|
+
one_ind = np.cross(one, one_k)[mu]
|
|
1031
|
+
if to_decr == "bra":
|
|
1032
|
+
expr += ket[k_] * one_ind * coulomb(N + 1, *bra, *(ket - one_k))
|
|
1033
|
+
else:
|
|
1034
|
+
expr += bra[k_] * one_ind * coulomb(N + 1, *(bra - one_k), *ket)
|
|
1035
|
+
return expr
|
|
1036
|
+
|
|
1037
|
+
# Base case
|
|
1038
|
+
if all([am == 0 for am in ang_moms]):
|
|
1039
|
+
AC = A - C
|
|
1040
|
+
BC = B - C
|
|
1041
|
+
ACxBC = AC.cross(BC)
|
|
1042
|
+
return 4 * p * ACxBC[m] * coulomb(N + 1, 0, 0, 0, 0, 0, 0)
|
|
1043
|
+
elif i > 0:
|
|
1044
|
+
return decr("bra", 0)
|
|
1045
|
+
elif j > 0:
|
|
1046
|
+
return decr("ket", 0)
|
|
1047
|
+
elif k > 0:
|
|
1048
|
+
return decr("bra", 1)
|
|
1049
|
+
elif l > 0:
|
|
1050
|
+
return decr("ket", 1)
|
|
1051
|
+
elif m > 0:
|
|
1052
|
+
return decr("bra", 2)
|
|
1053
|
+
elif n > 0:
|
|
1054
|
+
return decr("ket", 2)
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
class SpinOrbitShell(Function):
|
|
1058
|
+
@classmethod
|
|
1059
|
+
def eval(cls, La_tot, Lb_tot, a, b, A, B, C=(0.0, 0.0, 0.0)):
|
|
1060
|
+
exprs = [
|
|
1061
|
+
SpinOrbit(*La, *Lb, 0, mu, a, b, A, B, C)
|
|
1062
|
+
for (La, Lb), mu in it.product(shell_iter((La_tot, Lb_tot)), range(3))
|
|
1063
|
+
]
|
|
1064
|
+
# print(SpinOrbit.eval.cache_info())
|
|
1065
|
+
return exprs
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def get_center(i):
|
|
1069
|
+
symbs = [Symbol(str(i) + ind, real=True) for ind in ("x", "y", "z")]
|
|
1070
|
+
return Matrix([*symbs]).T # Return column vector
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
def get_centers(i):
|
|
1074
|
+
symbs = [Symbol(str(i) + ind, real=True) for ind in ("x", "y", "z")]
|
|
1075
|
+
return Matrix([*symbs]).T # Return column vector
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
def get_map(i, center_i):
|
|
1079
|
+
array = IndexedBase(i, shape=3)
|
|
1080
|
+
array_map = dict(zip(center_i, array))
|
|
1081
|
+
return array, array_map
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
def cart2spherical(L_tots, exprs):
|
|
1085
|
+
assert len(L_tots) > 0
|
|
1086
|
+
|
|
1087
|
+
# Coefficient matrices for Cartesian-to-spherical conversion
|
|
1088
|
+
coeffs = [Array(CART2SPH[L]) for L in L_tots]
|
|
1089
|
+
cart_shape = [(l + 1) * (l + 2) // 2 for l in L_tots]
|
|
1090
|
+
cart = Array(exprs).reshape(*cart_shape)
|
|
1091
|
+
|
|
1092
|
+
sph = tc(tp(coeffs[0], cart), (1, 2))
|
|
1093
|
+
if len(L_tots) == 2:
|
|
1094
|
+
sph = tc(tp(sph, coeffs[1].transpose()), (1, 2))
|
|
1095
|
+
elif len(L_tots) == 3:
|
|
1096
|
+
_, Cb, Cc = coeffs
|
|
1097
|
+
sph = tc(tp(permutedims(sph, (0, 2, 1)), Cb.transpose()), (2, 3))
|
|
1098
|
+
sph = tc(tp(permutedims(sph, (0, 2, 1)), Cc.transpose()), (2, 3))
|
|
1099
|
+
else:
|
|
1100
|
+
raise Exception(
|
|
1101
|
+
"Cartesian -> spherical transformation for 4-center integrals "
|
|
1102
|
+
"is not implemented!"
|
|
1103
|
+
)
|
|
1104
|
+
|
|
1105
|
+
# Cartesian-to-spherical transformation introduces quite a number of
|
|
1106
|
+
# multiplications by 1.0, which are uneccessary. Here, we try to drop
|
|
1107
|
+
# some of them by replacing numbers very close to +1.0 with 1.
|
|
1108
|
+
sph = sph.replace(lambda n: n.is_Number and (abs(n - 1) <= ONE_THRESH), lambda n: 1)
|
|
1109
|
+
# TODO: maybe something along the lines
|
|
1110
|
+
# sph = map(lambda expr: expr.evalf(), flatten(sph))
|
|
1111
|
+
# is faster?
|
|
1112
|
+
return flatten(sph)
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
def gen_integral_exprs(
|
|
1116
|
+
int_func,
|
|
1117
|
+
L_maxs,
|
|
1118
|
+
kind,
|
|
1119
|
+
maps=None,
|
|
1120
|
+
sph=False,
|
|
1121
|
+
):
|
|
1122
|
+
if maps is None:
|
|
1123
|
+
maps = list()
|
|
1124
|
+
|
|
1125
|
+
ranges = [range(L + 1) for L in L_maxs]
|
|
1126
|
+
|
|
1127
|
+
for L_tots in it.product(*ranges):
|
|
1128
|
+
time_str = time.strftime("%H:%M:%S")
|
|
1129
|
+
start = datetime.now()
|
|
1130
|
+
print(f"{time_str} - Generating {L_tots} {kind}")
|
|
1131
|
+
sys.stdout.flush()
|
|
1132
|
+
# Generate actual list of expressions.
|
|
1133
|
+
exprs = int_func(*L_tots)
|
|
1134
|
+
print("\t... generated expressions")
|
|
1135
|
+
sys.stdout.flush()
|
|
1136
|
+
# if sph:
|
|
1137
|
+
# exprs = cart2spherical(L_tots, exprs)
|
|
1138
|
+
# print("\t... did Cartesian -> Spherical conversion")
|
|
1139
|
+
# sys.stdout.flush()
|
|
1140
|
+
|
|
1141
|
+
# Common subexpression elimination
|
|
1142
|
+
repls, reduced = cse(list(exprs), order="none")
|
|
1143
|
+
print("\t... did common subexpression elimination")
|
|
1144
|
+
|
|
1145
|
+
# Replacement expressions, used to form the reduced expressions.
|
|
1146
|
+
for i, (lhs, rhs) in enumerate(repls):
|
|
1147
|
+
rhs = rhs.evalf()
|
|
1148
|
+
# Replace occurences of Ax, Ay, Az, ... with A[0], A[1], A[2], ...
|
|
1149
|
+
rhs = functools.reduce(lambda rhs, map_: rhs.xreplace(map_), maps, rhs)
|
|
1150
|
+
repls[i] = (lhs, rhs)
|
|
1151
|
+
|
|
1152
|
+
# Reduced expression, i.e., the final integrals/expressions.
|
|
1153
|
+
for i, red in enumerate(reduced):
|
|
1154
|
+
red = red.evalf()
|
|
1155
|
+
reduced[i] = functools.reduce(
|
|
1156
|
+
lambda red, map_: red.xreplace(map_), maps, red
|
|
1157
|
+
)
|
|
1158
|
+
# Carry out Cartesian-to-spherical transformation, if requested.
|
|
1159
|
+
if sph:
|
|
1160
|
+
reduced = cart2spherical(L_tots, reduced)
|
|
1161
|
+
print("\t... did Cartesian -> Spherical conversion")
|
|
1162
|
+
sys.stdout.flush()
|
|
1163
|
+
|
|
1164
|
+
dur = datetime.now() - start
|
|
1165
|
+
print(f"\t... finished in {str(dur)} h")
|
|
1166
|
+
sys.stdout.flush()
|
|
1167
|
+
yield (repls, reduced), L_tots
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
def render_py_funcs(exprs_Ls, args, base_name, doc_func, add_imports=None, comment=""):
|
|
1171
|
+
if add_imports is None:
|
|
1172
|
+
add_imports = ()
|
|
1173
|
+
add_imports_str = "\n".join(add_imports)
|
|
1174
|
+
if add_imports:
|
|
1175
|
+
add_imports_str += "\n\n"
|
|
1176
|
+
|
|
1177
|
+
args = ", ".join((map(str, args)))
|
|
1178
|
+
|
|
1179
|
+
py_funcs = list()
|
|
1180
|
+
for (repls, reduced), L_tots in exprs_Ls:
|
|
1181
|
+
doc_str = doc_func(L_tots)
|
|
1182
|
+
doc_str += "\n\nGenerated code; DO NOT modify by hand!"
|
|
1183
|
+
name = base_name + "_" + "".join(str(l) for l in L_tots)
|
|
1184
|
+
print(f"Rendering '{name}' ... ", end="")
|
|
1185
|
+
start = time.time()
|
|
1186
|
+
py_func = make_py_func(repls, reduced, args=args, name=name, doc_str=doc_str)
|
|
1187
|
+
dur = time.time() - start
|
|
1188
|
+
print(f"finished in {dur: >8.2f} s")
|
|
1189
|
+
py_funcs.append(py_func)
|
|
1190
|
+
py_funcs_joined = "\n\n".join(py_funcs)
|
|
1191
|
+
|
|
1192
|
+
if comment != "":
|
|
1193
|
+
comment = f'"""\n{comment}\n"""\n\n'
|
|
1194
|
+
|
|
1195
|
+
np_tpl = Template(
|
|
1196
|
+
"import numpy\n\n{{ add_imports }}{{ comment }}{{ py_funcs }}",
|
|
1197
|
+
trim_blocks=True,
|
|
1198
|
+
lstrip_blocks=True,
|
|
1199
|
+
)
|
|
1200
|
+
np_rendered = np_tpl.render(
|
|
1201
|
+
comment=comment, add_imports=add_imports_str, py_funcs=py_funcs_joined
|
|
1202
|
+
)
|
|
1203
|
+
np_rendered = textwrap.dedent(np_rendered)
|
|
1204
|
+
try:
|
|
1205
|
+
from black import format_str, FileMode
|
|
1206
|
+
|
|
1207
|
+
np_rendered = format_str(np_rendered, mode=FileMode(line_length=90))
|
|
1208
|
+
except ModuleNotFoundError:
|
|
1209
|
+
print("Skipped formatting with black, as it is not installed!")
|
|
1210
|
+
|
|
1211
|
+
return np_rendered
|
|
1212
|
+
|
|
1213
|
+
|
|
1214
|
+
def render_c_funcs(exprs_Ls, args, base_name, doc_func, add_imports=None, comment=""):
|
|
1215
|
+
if add_imports is not None:
|
|
1216
|
+
raise Exception("Implement me!")
|
|
1217
|
+
|
|
1218
|
+
arg_strs = [str(arg) for arg in args]
|
|
1219
|
+
|
|
1220
|
+
funcs = list()
|
|
1221
|
+
signatures = list()
|
|
1222
|
+
for (repls, reduced), L_tots in exprs_Ls:
|
|
1223
|
+
doc_str = doc_func(L_tots)
|
|
1224
|
+
doc_str += "\n\n\t\tGenerated code; DO NOT modify by hand!"
|
|
1225
|
+
doc_str = textwrap.dedent(doc_str)
|
|
1226
|
+
name = base_name + "_" + "".join(str(l) for l in L_tots)
|
|
1227
|
+
func, signature = make_c_func(
|
|
1228
|
+
repls, reduced, args=arg_strs, name=name, doc_str=doc_str
|
|
1229
|
+
)
|
|
1230
|
+
funcs.append(func)
|
|
1231
|
+
signatures.append(signature)
|
|
1232
|
+
funcs_joined = "\n\n".join(funcs)
|
|
1233
|
+
|
|
1234
|
+
if comment != "":
|
|
1235
|
+
comment = f"/*{comment}*/"
|
|
1236
|
+
|
|
1237
|
+
# Render C files
|
|
1238
|
+
c_tpl = Template(
|
|
1239
|
+
"#include <math.h>\n\n{{ comment }}\n\n{{ funcs }}",
|
|
1240
|
+
trim_blocks=True,
|
|
1241
|
+
lstrip_blocks=True,
|
|
1242
|
+
)
|
|
1243
|
+
c_rendered = c_tpl.render(comment=comment, funcs=funcs_joined)
|
|
1244
|
+
c_rendered = textwrap.dedent(c_rendered)
|
|
1245
|
+
# Render simple header file
|
|
1246
|
+
h_rendered = "\n".join([f"{sig};" for sig in signatures])
|
|
1247
|
+
return c_rendered, h_rendered
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
def write_file(out_dir, name, rendered):
|
|
1251
|
+
out_name = out_dir / name
|
|
1252
|
+
with open(out_name, "w") as handle:
|
|
1253
|
+
handle.write(rendered)
|
|
1254
|
+
print(f"Wrote '{out_name}'.")
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
def write_render(
|
|
1258
|
+
ints_Ls,
|
|
1259
|
+
args,
|
|
1260
|
+
name,
|
|
1261
|
+
doc_func,
|
|
1262
|
+
out_dir,
|
|
1263
|
+
comment="",
|
|
1264
|
+
py_kwargs=None,
|
|
1265
|
+
c=True,
|
|
1266
|
+
c_kwargs=None,
|
|
1267
|
+
):
|
|
1268
|
+
if py_kwargs is None:
|
|
1269
|
+
py_kwargs = {}
|
|
1270
|
+
if c_kwargs is None:
|
|
1271
|
+
c_kwargs = {}
|
|
1272
|
+
ints_Ls = list(ints_Ls)
|
|
1273
|
+
# Python
|
|
1274
|
+
py_rendered = render_py_funcs(
|
|
1275
|
+
ints_Ls, args, name, doc_func, comment=comment, **py_kwargs
|
|
1276
|
+
)
|
|
1277
|
+
write_file(out_dir, f"{name}.py", py_rendered)
|
|
1278
|
+
# C
|
|
1279
|
+
if c:
|
|
1280
|
+
c_rendered, h_rendered = render_c_funcs(
|
|
1281
|
+
ints_Ls,
|
|
1282
|
+
args,
|
|
1283
|
+
name,
|
|
1284
|
+
doc_func,
|
|
1285
|
+
comment=comment,
|
|
1286
|
+
**c_kwargs,
|
|
1287
|
+
)
|
|
1288
|
+
write_file(out_dir, f"{name}.c", c_rendered)
|
|
1289
|
+
write_file(out_dir, f"{name}.h", h_rendered)
|
|
1290
|
+
|
|
1291
|
+
|
|
1292
|
+
def parse_args(args):
|
|
1293
|
+
parser = argparse.ArgumentParser()
|
|
1294
|
+
|
|
1295
|
+
parser.add_argument(
|
|
1296
|
+
"--lmax",
|
|
1297
|
+
default=L_MAX,
|
|
1298
|
+
type=int,
|
|
1299
|
+
help="Generate 1e-integrals up to this maximum angular momentum.",
|
|
1300
|
+
)
|
|
1301
|
+
parser.add_argument(
|
|
1302
|
+
"--lauxmax",
|
|
1303
|
+
default=L_AUX_MAX,
|
|
1304
|
+
type=int,
|
|
1305
|
+
help="Maximum angular moment for integrals using auxiliary functions.",
|
|
1306
|
+
)
|
|
1307
|
+
parser.add_argument(
|
|
1308
|
+
"--write",
|
|
1309
|
+
action="store_true",
|
|
1310
|
+
help="Write out generated integrals to the current directory, potentially overwriting the present modules.",
|
|
1311
|
+
)
|
|
1312
|
+
parser.add_argument(
|
|
1313
|
+
"--out-dir",
|
|
1314
|
+
default="devel_ints",
|
|
1315
|
+
help="Directory, where the generated integrals are written.",
|
|
1316
|
+
)
|
|
1317
|
+
keys_str = f"({', '.join(KEYS)})"
|
|
1318
|
+
parser.add_argument(
|
|
1319
|
+
"--keys",
|
|
1320
|
+
nargs="+",
|
|
1321
|
+
help=f"Generate only certain expressions. Possible keys are: {keys_str}. "
|
|
1322
|
+
"If not given, all expressions are generated.",
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
return parser.parse_args()
|
|
1326
|
+
|
|
1327
|
+
|
|
1328
|
+
def run():
|
|
1329
|
+
args = parse_args(sys.argv[1:])
|
|
1330
|
+
|
|
1331
|
+
l_max = args.lmax
|
|
1332
|
+
l_aux_max = args.lauxmax
|
|
1333
|
+
out_dir = Path(args.out_dir if not args.write else ".")
|
|
1334
|
+
keys = args.keys
|
|
1335
|
+
if keys is None:
|
|
1336
|
+
keys = list()
|
|
1337
|
+
try:
|
|
1338
|
+
os.mkdir(out_dir)
|
|
1339
|
+
except FileExistsError:
|
|
1340
|
+
pass
|
|
1341
|
+
|
|
1342
|
+
try:
|
|
1343
|
+
global CART2SPH
|
|
1344
|
+
CART2SPH = cart2sph_coeffs(max(l_max, l_aux_max), zero_small=True)
|
|
1345
|
+
except NameError:
|
|
1346
|
+
print("cart2sph_coeffs import is deactivated or pysisyphus is not installed.")
|
|
1347
|
+
|
|
1348
|
+
# Cartesian basis function centers A and B.
|
|
1349
|
+
center_A = get_center("A")
|
|
1350
|
+
center_B = get_center("B")
|
|
1351
|
+
center_C = get_center("C")
|
|
1352
|
+
center_D = get_center("D")
|
|
1353
|
+
# center_R = get_center("R")
|
|
1354
|
+
Xa, Ya, Za = symbols("Xa Ya Za")
|
|
1355
|
+
|
|
1356
|
+
# Orbital exponents a, b, c, d.
|
|
1357
|
+
a, b, c, d = symbols("a b c d", real=True)
|
|
1358
|
+
|
|
1359
|
+
# These maps will be used to convert {Ax, Ay, ...} to array quantities
|
|
1360
|
+
# in the generated code. This way an iterable/np.ndarray can be used as
|
|
1361
|
+
# function argument instead of (Ax, Ay, Az, Bx, By, Bz).
|
|
1362
|
+
A, A_map = get_map("A", center_A)
|
|
1363
|
+
B, B_map = get_map("B", center_B)
|
|
1364
|
+
C, C_map = get_map("C", center_C)
|
|
1365
|
+
D, D_map = get_map("D", center_D)
|
|
1366
|
+
|
|
1367
|
+
boys_import = ("from pysisyphus.wavefunction.ints.boys import boys",)
|
|
1368
|
+
|
|
1369
|
+
#################
|
|
1370
|
+
# Cartesian GTO #
|
|
1371
|
+
#################
|
|
1372
|
+
|
|
1373
|
+
def cart_gto():
|
|
1374
|
+
def cart_gto_doc_func(L_tot):
|
|
1375
|
+
(La_tot,) = L_tot
|
|
1376
|
+
shell_a = L_MAP[La_tot]
|
|
1377
|
+
return (
|
|
1378
|
+
f"3D Cartesian {shell_a}-Gaussian shell.\n\n"
|
|
1379
|
+
"Exponent a, centered at A, evaluated at (Xa, Ya, Za) + A."
|
|
1380
|
+
)
|
|
1381
|
+
|
|
1382
|
+
# This code can evaluate multiple points at a time
|
|
1383
|
+
cart_gto_Ls = gen_integral_exprs(
|
|
1384
|
+
lambda La_tot: CartGTOShell(La_tot, a, Xa, Ya, Za),
|
|
1385
|
+
(l_max,),
|
|
1386
|
+
"cart_gto",
|
|
1387
|
+
)
|
|
1388
|
+
cart_gto_rendered = render_py_funcs(
|
|
1389
|
+
cart_gto_Ls, (a, Xa, Ya, Za), "cart_gto3d", cart_gto_doc_func
|
|
1390
|
+
)
|
|
1391
|
+
write_file(out_dir, "gto3d.py", cart_gto_rendered)
|
|
1392
|
+
print()
|
|
1393
|
+
|
|
1394
|
+
#####################
|
|
1395
|
+
# Overlap integrals #
|
|
1396
|
+
#####################
|
|
1397
|
+
|
|
1398
|
+
def overlap():
|
|
1399
|
+
def ovlp_doc_func(L_tots):
|
|
1400
|
+
La_tot, Lb_tot = L_tots
|
|
1401
|
+
shell_a = L_MAP[La_tot]
|
|
1402
|
+
shell_b = L_MAP[Lb_tot]
|
|
1403
|
+
return f"Cartesian 3D ({shell_a}{shell_b}) overlap integral."
|
|
1404
|
+
|
|
1405
|
+
ovlp_ints_Ls = gen_integral_exprs(
|
|
1406
|
+
lambda La_tot, Lb_tot: Overlap3dShell(
|
|
1407
|
+
La_tot, Lb_tot, a, b, center_A, center_B
|
|
1408
|
+
),
|
|
1409
|
+
(l_max, l_max),
|
|
1410
|
+
"overlap",
|
|
1411
|
+
(A_map, B_map),
|
|
1412
|
+
)
|
|
1413
|
+
write_render(
|
|
1414
|
+
ovlp_ints_Ls, (a, A, b, B), "ovlp3d", ovlp_doc_func, out_dir, c=True
|
|
1415
|
+
)
|
|
1416
|
+
print()
|
|
1417
|
+
|
|
1418
|
+
###########################
|
|
1419
|
+
# Dipole moment integrals #
|
|
1420
|
+
###########################
|
|
1421
|
+
|
|
1422
|
+
def dipole():
|
|
1423
|
+
def dipole_doc_func(L_tots):
|
|
1424
|
+
La_tot, Lb_tot = L_tots
|
|
1425
|
+
shell_a = L_MAP[La_tot]
|
|
1426
|
+
shell_b = L_MAP[Lb_tot]
|
|
1427
|
+
return (
|
|
1428
|
+
f"Cartesian 3D ({shell_a}{shell_b}) dipole moment integrals.\n"
|
|
1429
|
+
"The origin is at C."
|
|
1430
|
+
)
|
|
1431
|
+
|
|
1432
|
+
dipole_comment = """
|
|
1433
|
+
Dipole integrals are given in the order:
|
|
1434
|
+
for bf_a in basis_functions_a:
|
|
1435
|
+
for bf_b in basis_functions_b:
|
|
1436
|
+
for cart_dir in (x, y, z):
|
|
1437
|
+
dipole_integrals(bf_a, bf_b, cart_dir)
|
|
1438
|
+
|
|
1439
|
+
So for <s_a|μ|s_b> it will be:
|
|
1440
|
+
|
|
1441
|
+
<s_a|x|s_b>
|
|
1442
|
+
<s_a|y|s_b>
|
|
1443
|
+
<s_a|z|s_b>
|
|
1444
|
+
"""
|
|
1445
|
+
|
|
1446
|
+
dipole_ints_Ls = gen_integral_exprs(
|
|
1447
|
+
lambda La_tot, Lb_tot: Multipole3dShell(
|
|
1448
|
+
La_tot, Lb_tot, a, b, center_A, center_B, 1, center_C
|
|
1449
|
+
),
|
|
1450
|
+
(l_max, l_max),
|
|
1451
|
+
"dipole moment",
|
|
1452
|
+
(A_map, B_map, C_map),
|
|
1453
|
+
)
|
|
1454
|
+
write_render(
|
|
1455
|
+
dipole_ints_Ls,
|
|
1456
|
+
(a, A, b, B, C),
|
|
1457
|
+
"dipole3d",
|
|
1458
|
+
dipole_doc_func,
|
|
1459
|
+
out_dir,
|
|
1460
|
+
comment=dipole_comment,
|
|
1461
|
+
c=True,
|
|
1462
|
+
)
|
|
1463
|
+
print()
|
|
1464
|
+
|
|
1465
|
+
###########################################
|
|
1466
|
+
# Diagonal of quadrupole moment integrals #
|
|
1467
|
+
###########################################
|
|
1468
|
+
|
|
1469
|
+
def diag_quadrupole():
|
|
1470
|
+
def diag_quadrupole_doc_func(L_tots):
|
|
1471
|
+
La_tot, Lb_tot = L_tots
|
|
1472
|
+
shell_a = L_MAP[La_tot]
|
|
1473
|
+
shell_b = L_MAP[Lb_tot]
|
|
1474
|
+
return (
|
|
1475
|
+
f"Cartesian 3D ({shell_a}{shell_b}) quadrupole moment integrals\n"
|
|
1476
|
+
"for operators x², y² and z². The origin is at C."
|
|
1477
|
+
)
|
|
1478
|
+
|
|
1479
|
+
diag_quadrupole_comment = """
|
|
1480
|
+
Diagonal of the quadrupole moment matrix with operators x², y², z².
|
|
1481
|
+
|
|
1482
|
+
for rr in (xx, yy, zz):
|
|
1483
|
+
for bf_a in basis_functions_a:
|
|
1484
|
+
for bf_b in basis_functions_b:
|
|
1485
|
+
quadrupole_integrals(bf_a, bf_b, rr)
|
|
1486
|
+
"""
|
|
1487
|
+
|
|
1488
|
+
diag_quadrupole_ints_Ls = gen_integral_exprs(
|
|
1489
|
+
lambda La_tot, Lb_tot: DiagQuadrupole3dShell(
|
|
1490
|
+
La_tot, Lb_tot, a, b, center_A, center_B, center_C
|
|
1491
|
+
),
|
|
1492
|
+
(l_max, l_max),
|
|
1493
|
+
"diag quadrupole moment",
|
|
1494
|
+
(A_map, B_map, C_map),
|
|
1495
|
+
)
|
|
1496
|
+
write_render(
|
|
1497
|
+
diag_quadrupole_ints_Ls,
|
|
1498
|
+
(a, A, b, B, C),
|
|
1499
|
+
"diag_quadrupole3d",
|
|
1500
|
+
diag_quadrupole_doc_func,
|
|
1501
|
+
out_dir,
|
|
1502
|
+
comment=diag_quadrupole_comment,
|
|
1503
|
+
c=True,
|
|
1504
|
+
)
|
|
1505
|
+
print()
|
|
1506
|
+
|
|
1507
|
+
###############################
|
|
1508
|
+
# Quadrupole moment integrals #
|
|
1509
|
+
###############################
|
|
1510
|
+
|
|
1511
|
+
def quadrupole():
|
|
1512
|
+
def quadrupole_doc_func(L_tots):
|
|
1513
|
+
La_tot, Lb_tot = L_tots
|
|
1514
|
+
shell_a = L_MAP[La_tot]
|
|
1515
|
+
shell_b = L_MAP[Lb_tot]
|
|
1516
|
+
return (
|
|
1517
|
+
f"Cartesian 3D ({shell_a}{shell_b}) quadrupole moment integrals.\n"
|
|
1518
|
+
"The origin is at C."
|
|
1519
|
+
)
|
|
1520
|
+
|
|
1521
|
+
quadrupole_comment = """
|
|
1522
|
+
Quadrupole integrals contain the upper triangular part of the symmetric
|
|
1523
|
+
3x3 quadrupole matrix.
|
|
1524
|
+
|
|
1525
|
+
/ xx xy xz \\
|
|
1526
|
+
| yy yz |
|
|
1527
|
+
\ zz /
|
|
1528
|
+
"""
|
|
1529
|
+
|
|
1530
|
+
quadrupole_ints_Ls = gen_integral_exprs(
|
|
1531
|
+
lambda La_tot, Lb_tot: Multipole3dShell(
|
|
1532
|
+
La_tot, Lb_tot, a, b, center_A, center_B, 2, center_C
|
|
1533
|
+
),
|
|
1534
|
+
(l_max, l_max),
|
|
1535
|
+
"quadrupole moment",
|
|
1536
|
+
(A_map, B_map, C_map),
|
|
1537
|
+
)
|
|
1538
|
+
|
|
1539
|
+
write_render(
|
|
1540
|
+
quadrupole_ints_Ls,
|
|
1541
|
+
(a, A, b, B, C),
|
|
1542
|
+
"quadrupole3d",
|
|
1543
|
+
quadrupole_doc_func,
|
|
1544
|
+
out_dir,
|
|
1545
|
+
comment=quadrupole_comment,
|
|
1546
|
+
c=True,
|
|
1547
|
+
)
|
|
1548
|
+
print()
|
|
1549
|
+
|
|
1550
|
+
############################
|
|
1551
|
+
# Kinetic energy integrals #
|
|
1552
|
+
############################
|
|
1553
|
+
|
|
1554
|
+
def kinetic():
|
|
1555
|
+
def kinetic_doc_func(L_tots):
|
|
1556
|
+
La_tot, Lb_tot = L_tots
|
|
1557
|
+
shell_a = L_MAP[La_tot]
|
|
1558
|
+
shell_b = L_MAP[Lb_tot]
|
|
1559
|
+
return f"Cartesian 3D ({shell_a}{shell_b}) kinetic energy integral."
|
|
1560
|
+
|
|
1561
|
+
kinetic_ints_Ls = gen_integral_exprs(
|
|
1562
|
+
lambda La_tot, Lb_tot: Kinetic3dShell(
|
|
1563
|
+
La_tot, Lb_tot, a, b, center_A, center_B
|
|
1564
|
+
),
|
|
1565
|
+
(l_max, l_max),
|
|
1566
|
+
"kinetic",
|
|
1567
|
+
(A_map, B_map),
|
|
1568
|
+
)
|
|
1569
|
+
write_render(
|
|
1570
|
+
kinetic_ints_Ls,
|
|
1571
|
+
(a, A, b, B),
|
|
1572
|
+
"kinetic3d",
|
|
1573
|
+
kinetic_doc_func,
|
|
1574
|
+
out_dir,
|
|
1575
|
+
c=True,
|
|
1576
|
+
)
|
|
1577
|
+
print()
|
|
1578
|
+
|
|
1579
|
+
#########################
|
|
1580
|
+
# 1el Coulomb Integrals #
|
|
1581
|
+
#########################
|
|
1582
|
+
|
|
1583
|
+
def coulomb():
|
|
1584
|
+
def coulomb_doc_func(L_tots):
|
|
1585
|
+
La_tot, Lb_tot = L_tots
|
|
1586
|
+
shell_a = L_MAP[La_tot]
|
|
1587
|
+
shell_b = L_MAP[Lb_tot]
|
|
1588
|
+
return f"Cartesian ({shell_a}{shell_b}) 1-electron Coulomb integral."
|
|
1589
|
+
|
|
1590
|
+
coulomb_ints_Ls = gen_integral_exprs(
|
|
1591
|
+
lambda La_tot, Lb_tot: CoulombShell(
|
|
1592
|
+
La_tot, Lb_tot, a, b, center_A, center_B, center_C
|
|
1593
|
+
),
|
|
1594
|
+
(l_max, l_max),
|
|
1595
|
+
"coulomb3d",
|
|
1596
|
+
(A_map, B_map, C_map),
|
|
1597
|
+
)
|
|
1598
|
+
coulomb_rendered = render_py_funcs(
|
|
1599
|
+
coulomb_ints_Ls,
|
|
1600
|
+
(a, A, b, B, C),
|
|
1601
|
+
"coulomb3d",
|
|
1602
|
+
coulomb_doc_func,
|
|
1603
|
+
add_imports=boys_import,
|
|
1604
|
+
)
|
|
1605
|
+
# TODO: handle add_args and Boys function in C
|
|
1606
|
+
write_file(out_dir, "coulomb3d.py", coulomb_rendered)
|
|
1607
|
+
print()
|
|
1608
|
+
|
|
1609
|
+
###############################################
|
|
1610
|
+
# Two-center two-electron repulsion integrals #
|
|
1611
|
+
###############################################
|
|
1612
|
+
|
|
1613
|
+
def _2center2electron():
|
|
1614
|
+
def _2center2el_doc_func(L_tots):
|
|
1615
|
+
La_tot, Lb_tot = L_tots
|
|
1616
|
+
shell_a = L_MAP[La_tot]
|
|
1617
|
+
shell_b = L_MAP[Lb_tot]
|
|
1618
|
+
return (
|
|
1619
|
+
f"Cartesian ({shell_a}|{shell_b}) "
|
|
1620
|
+
"two-center two-electron repulsion integral."
|
|
1621
|
+
)
|
|
1622
|
+
|
|
1623
|
+
_2center2el_ints_Ls = gen_integral_exprs(
|
|
1624
|
+
lambda La_tot, Lb_tot: TwoCenterTwoElectronShell(
|
|
1625
|
+
La_tot,
|
|
1626
|
+
Lb_tot,
|
|
1627
|
+
a,
|
|
1628
|
+
b,
|
|
1629
|
+
center_A,
|
|
1630
|
+
center_B,
|
|
1631
|
+
),
|
|
1632
|
+
(l_aux_max, l_aux_max),
|
|
1633
|
+
"_2center2el3d",
|
|
1634
|
+
(A_map, B_map),
|
|
1635
|
+
)
|
|
1636
|
+
write_render(
|
|
1637
|
+
_2center2el_ints_Ls,
|
|
1638
|
+
(a, A, b, B),
|
|
1639
|
+
"_2center2el3d",
|
|
1640
|
+
_2center2el_doc_func,
|
|
1641
|
+
out_dir,
|
|
1642
|
+
c=False,
|
|
1643
|
+
py_kwargs={"add_imports": boys_import},
|
|
1644
|
+
)
|
|
1645
|
+
print()
|
|
1646
|
+
|
|
1647
|
+
#################################################
|
|
1648
|
+
# Three-center two-electron repulsion integrals #
|
|
1649
|
+
#################################################
|
|
1650
|
+
|
|
1651
|
+
def _3center2electron():
|
|
1652
|
+
def _3center2el_doc_func(L_tots):
|
|
1653
|
+
La_tot, Lb_tot, Lc_tot = L_tots
|
|
1654
|
+
shell_a = L_MAP[La_tot]
|
|
1655
|
+
shell_b = L_MAP[Lb_tot]
|
|
1656
|
+
shell_c = L_MAP[Lc_tot]
|
|
1657
|
+
return (
|
|
1658
|
+
f"Cartesian ({shell_a}{shell_b}|{shell_c}) "
|
|
1659
|
+
"three-center two-electron repulsion integral."
|
|
1660
|
+
)
|
|
1661
|
+
|
|
1662
|
+
_3center2el_ints_Ls = gen_integral_exprs(
|
|
1663
|
+
lambda La_tot, Lb_tot, Lc_tot: ThreeCenterTwoElectronShell(
|
|
1664
|
+
La_tot, Lb_tot, Lc_tot, a, b, c, center_A, center_B, center_C
|
|
1665
|
+
),
|
|
1666
|
+
(l_max, l_max, l_aux_max),
|
|
1667
|
+
"_3center2el3d",
|
|
1668
|
+
(A_map, B_map, C_map),
|
|
1669
|
+
)
|
|
1670
|
+
write_render(
|
|
1671
|
+
_3center2el_ints_Ls,
|
|
1672
|
+
(a, A, b, B, c, C),
|
|
1673
|
+
"_3center2el3d",
|
|
1674
|
+
_3center2el_doc_func,
|
|
1675
|
+
out_dir,
|
|
1676
|
+
c=False,
|
|
1677
|
+
py_kwargs={"add_imports": boys_import},
|
|
1678
|
+
)
|
|
1679
|
+
print()
|
|
1680
|
+
|
|
1681
|
+
def _3center2electron_sph():
|
|
1682
|
+
def _3center2el_doc_func(L_tots):
|
|
1683
|
+
La_tot, Lb_tot, Lc_tot = L_tots
|
|
1684
|
+
shell_a = L_MAP[La_tot]
|
|
1685
|
+
shell_b = L_MAP[Lb_tot]
|
|
1686
|
+
shell_c = L_MAP[Lc_tot]
|
|
1687
|
+
return (
|
|
1688
|
+
f"Cartesian ({shell_a}{shell_b}|{shell_c}) "
|
|
1689
|
+
"three-center two-electron repulsion integral for conversion to "
|
|
1690
|
+
"spherical harmonics.\nUses Ahlrichs' vertical recursion relation, "
|
|
1691
|
+
"that leaves out some terms, that cancel\nwhen convertig to "
|
|
1692
|
+
"spherical harmonics."
|
|
1693
|
+
)
|
|
1694
|
+
|
|
1695
|
+
_3center2el_ints_Ls = gen_integral_exprs(
|
|
1696
|
+
lambda La_tot, Lb_tot, Lc_tot: ThreeCenterTwoElectronSphShell(
|
|
1697
|
+
La_tot, Lb_tot, Lc_tot, a, b, c, center_A, center_B, center_C
|
|
1698
|
+
),
|
|
1699
|
+
(l_max, l_max, l_aux_max),
|
|
1700
|
+
"_3center2el3d_sph",
|
|
1701
|
+
(A_map, B_map, C_map),
|
|
1702
|
+
sph=False,
|
|
1703
|
+
)
|
|
1704
|
+
write_render(
|
|
1705
|
+
_3center2el_ints_Ls,
|
|
1706
|
+
(a, A, b, B, c, C),
|
|
1707
|
+
"_3center2el3d_sph",
|
|
1708
|
+
_3center2el_doc_func,
|
|
1709
|
+
out_dir,
|
|
1710
|
+
c=False,
|
|
1711
|
+
py_kwargs={"add_imports": boys_import},
|
|
1712
|
+
)
|
|
1713
|
+
print()
|
|
1714
|
+
|
|
1715
|
+
####################################
|
|
1716
|
+
# Spin-orbit interaction integrals #
|
|
1717
|
+
####################################
|
|
1718
|
+
|
|
1719
|
+
# def so1el_doc_func(L_tots):
|
|
1720
|
+
# La_tot, Lb_tot = L_tots
|
|
1721
|
+
# shell_a = L_MAP[La_tot]
|
|
1722
|
+
# shell_b = L_MAP[Lb_tot]
|
|
1723
|
+
# return f"Cartesian ({shell_a}{shell_b}) 1-electron spin-orbit-interaction integral."
|
|
1724
|
+
|
|
1725
|
+
# I think this is still faulty!
|
|
1726
|
+
# so1el_ints_Ls = gen_integral_exprs(
|
|
1727
|
+
# lambda La_tot, Lb_tot: SpinOrbitShell(
|
|
1728
|
+
# La_tot, Lb_tot, a, b, center_A, center_B, center_C
|
|
1729
|
+
# ),
|
|
1730
|
+
# (l_max, l_max),
|
|
1731
|
+
# "so1el",
|
|
1732
|
+
# (A_map, B_map, C_map),
|
|
1733
|
+
# )
|
|
1734
|
+
|
|
1735
|
+
# so1el_rendered = render_py_funcs(
|
|
1736
|
+
# so1el_ints_Ls,
|
|
1737
|
+
# (a, A, b, B, C),
|
|
1738
|
+
# "so1el",
|
|
1739
|
+
# so1el_doc_func,
|
|
1740
|
+
# add_imports=boys_import,
|
|
1741
|
+
# )
|
|
1742
|
+
# write_file(out_dir, "so1el.py", so1el_rendered)
|
|
1743
|
+
# print()
|
|
1744
|
+
|
|
1745
|
+
#########################
|
|
1746
|
+
# 2el Coulomb Integrals #
|
|
1747
|
+
#########################
|
|
1748
|
+
|
|
1749
|
+
# def eri_doc_func(L_tots):
|
|
1750
|
+
# La_tot, Lb_tot, Lc_tot, Ld_tot = L_tots
|
|
1751
|
+
# shell_a = L_MAP[La_tot]
|
|
1752
|
+
# shell_b = L_MAP[Lb_tot]
|
|
1753
|
+
# shell_c = L_MAP[Lc_tot]
|
|
1754
|
+
# shell_d = L_MAP[Ld_tot]
|
|
1755
|
+
# return (
|
|
1756
|
+
# f"Cartesian [{shell_a}{shell_b}|{shell_c}{shell_d}] "
|
|
1757
|
+
# "2-electron electron repulsion integral."
|
|
1758
|
+
# )
|
|
1759
|
+
|
|
1760
|
+
# I think this is still faulty!
|
|
1761
|
+
# eri_ints_Ls = gen_integral_exprs(
|
|
1762
|
+
# lambda La_tot, Lb_tot, Lc_tot, Ld_tot: ERIShell(
|
|
1763
|
+
# La_tot,
|
|
1764
|
+
# Lb_tot,
|
|
1765
|
+
# Lc_tot,
|
|
1766
|
+
# Ld_tot,
|
|
1767
|
+
# a,
|
|
1768
|
+
# b,
|
|
1769
|
+
# c,
|
|
1770
|
+
# d,
|
|
1771
|
+
# center_A,
|
|
1772
|
+
# center_B,
|
|
1773
|
+
# center_C,
|
|
1774
|
+
# center_D,
|
|
1775
|
+
# ),
|
|
1776
|
+
# # (l_max, l_max, l_max, l_max),
|
|
1777
|
+
# (2, 2, 2, 2), # Stop at [dd|dd] for now
|
|
1778
|
+
# "eri",
|
|
1779
|
+
# (A_map, B_map, C_map, D_map),
|
|
1780
|
+
# )
|
|
1781
|
+
# eri_rendered = render_py_funcs(
|
|
1782
|
+
# eri_ints_Ls,
|
|
1783
|
+
# (a, A, b, B, c, C, d, D),
|
|
1784
|
+
# "eri",
|
|
1785
|
+
# eri_doc_func,
|
|
1786
|
+
# add_imports=boys_import,
|
|
1787
|
+
# )
|
|
1788
|
+
# write_file(out_dir, "eri.py", eri_rendered)
|
|
1789
|
+
# print()
|
|
1790
|
+
|
|
1791
|
+
funcs = {
|
|
1792
|
+
"cgto": cart_gto, # Cartesian Gaussian-type-orbital for density evaluation
|
|
1793
|
+
"ovlp": overlap, # Overlap integrals
|
|
1794
|
+
"dpm": dipole, # Linear moment (dipole) integrals
|
|
1795
|
+
"dqpm": diag_quadrupole, # Diagonal part of the quadrupole tensor
|
|
1796
|
+
"qpm": quadrupole, # Quadratic moment (quadrupole) integrals
|
|
1797
|
+
"kin": kinetic, # Kinetic energy integrals
|
|
1798
|
+
"coul": coulomb, # 1-electron Coulomb integrals
|
|
1799
|
+
# Integrals for density fitting
|
|
1800
|
+
"2c2e": _2center2electron, # 2-center-2-electron integrals for DF
|
|
1801
|
+
"3c2e": _3center2electron, # 3-center-2-electron integrals for density fitting
|
|
1802
|
+
"3c2e_sph": _3center2electron_sph, # 3c2el for spherical transformation for DF
|
|
1803
|
+
}
|
|
1804
|
+
if len(keys) == 0:
|
|
1805
|
+
keys = funcs.keys()
|
|
1806
|
+
for key in keys:
|
|
1807
|
+
funcs[key]()
|
|
1808
|
+
|
|
1809
|
+
|
|
1810
|
+
if __name__ == "__main__":
|
|
1811
|
+
run()
|