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,504 @@
|
|
|
1
|
+
# [1] https://pubs.acs.org/doi/pdf/10.1021/j100180a030.
|
|
2
|
+
# Toward a Systematic Molecular Orbital Theory for Excited States
|
|
3
|
+
# Foresman, Head-Gordon, Pople, Frisch, 1991
|
|
4
|
+
|
|
5
|
+
import operator
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Dict, Literal, List, Optional, Tuple
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from numpy.typing import NDArray
|
|
12
|
+
|
|
13
|
+
from pysisyphus.elem_data import nuc_charges_for_atoms, MASS_DICT
|
|
14
|
+
from pysisyphus.Geometry import Geometry
|
|
15
|
+
from pysisyphus.helpers_pure import file_or_str
|
|
16
|
+
from pysisyphus.wavefunction.helpers import BFType
|
|
17
|
+
from pysisyphus.wavefunction.multipole import (
|
|
18
|
+
get_multipole_moment,
|
|
19
|
+
get_transition_multipole_moment,
|
|
20
|
+
)
|
|
21
|
+
from pysisyphus.wavefunction.shells import Shells
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Center = Literal["coc", "com"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Wavefunction:
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
atoms: Tuple[str],
|
|
31
|
+
coords: NDArray[float],
|
|
32
|
+
charge: int,
|
|
33
|
+
mult: int,
|
|
34
|
+
unrestricted: bool,
|
|
35
|
+
occ: Tuple[int],
|
|
36
|
+
C: NDArray[float],
|
|
37
|
+
bf_type: BFType,
|
|
38
|
+
shells: Optional[Shells] = None,
|
|
39
|
+
ecp_electrons=None,
|
|
40
|
+
strict=True,
|
|
41
|
+
warn_charge=4,
|
|
42
|
+
):
|
|
43
|
+
self.atoms = atoms
|
|
44
|
+
self.coords = np.array(coords).flatten()
|
|
45
|
+
assert 3 * len(self.atoms) == len(self.coords)
|
|
46
|
+
|
|
47
|
+
self.mult = mult
|
|
48
|
+
if self.mult != 1:
|
|
49
|
+
unrestricted = True
|
|
50
|
+
self.unrestricted = unrestricted
|
|
51
|
+
self.occ = occ
|
|
52
|
+
if not self.unrestricted:
|
|
53
|
+
assert self.occ[0] == self.occ[1]
|
|
54
|
+
# MOs are stored in columns
|
|
55
|
+
self.C = np.array(C)
|
|
56
|
+
# Always keep α and β coefficients separate. If we get only one set
|
|
57
|
+
# of MO coefficients (one square matrix with ndim == 2) we assume a
|
|
58
|
+
# restricted calculation and use the same set for alpha and beta electrons.
|
|
59
|
+
if C.ndim == 2:
|
|
60
|
+
self.C = np.array((self.C, self.C.copy()))
|
|
61
|
+
self.bf_type = bf_type
|
|
62
|
+
self.shells = shells
|
|
63
|
+
if ecp_electrons is None:
|
|
64
|
+
ecp_electrons = np.zeros(len(self.atoms))
|
|
65
|
+
elif isinstance(ecp_electrons, dict):
|
|
66
|
+
_ecp_electrons = np.zeros(len(self.atoms))
|
|
67
|
+
for k, v in ecp_electrons.items():
|
|
68
|
+
_ecp_electrons[k] = v
|
|
69
|
+
ecp_electrons = _ecp_electrons
|
|
70
|
+
self.ecp_electrons = np.array(ecp_electrons, dtype=int)
|
|
71
|
+
self.charge = charge
|
|
72
|
+
if abs(self.charge) > warn_charge:
|
|
73
|
+
warnings.warn(
|
|
74
|
+
f"Encountered charge={self.charge} with high absolute value!\n"
|
|
75
|
+
"This may happen in systems with ECPs. In such cases please set "
|
|
76
|
+
"'ecp_electrons' or at least the correct total charge."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
self._masses = np.array([MASS_DICT[atom.lower()] for atom in self.atoms])
|
|
80
|
+
self._densities = dict()
|
|
81
|
+
|
|
82
|
+
if strict:
|
|
83
|
+
self.check_sanity()
|
|
84
|
+
|
|
85
|
+
def check_sanity(self):
|
|
86
|
+
assert self.shells is not None
|
|
87
|
+
S = self.S # Overlap matrix
|
|
88
|
+
diag_S = np.diag(S)
|
|
89
|
+
np.testing.assert_allclose(diag_S, np.ones_like(diag_S), atol=1e-12)
|
|
90
|
+
Pa_mo, Pb_mo = [
|
|
91
|
+
C.T @ S @ P @ S @ C for C, P in zip(self.C, self.P)
|
|
92
|
+
] # Density matrices in MO basis
|
|
93
|
+
calc_occ_a = np.trace(Pa_mo)
|
|
94
|
+
calc_occ_b = np.trace(Pb_mo)
|
|
95
|
+
assert np.isclose((calc_occ_a, calc_occ_b), self.occ).all()
|
|
96
|
+
|
|
97
|
+
assert (
|
|
98
|
+
self.ecp_electrons >= 0
|
|
99
|
+
).all(), "All entries in 'ecp_electrons' must be >= 0!"
|
|
100
|
+
# We can't use self.nuc_charges, as it already contains ecp_electrons
|
|
101
|
+
electrons_expected = (
|
|
102
|
+
nuc_charges_for_atoms(self.atoms).sum()
|
|
103
|
+
- self.charge # Negative charge means MORE electrons, not less!
|
|
104
|
+
- self.ecp_electrons.sum()
|
|
105
|
+
)
|
|
106
|
+
electrons_present = sum(self.occ)
|
|
107
|
+
electrons_missing = electrons_expected - electrons_present
|
|
108
|
+
assert electrons_missing == 0, (
|
|
109
|
+
f"{electrons_missing} electrons are missing! Did you forget to specify "
|
|
110
|
+
"'ecp_electrons' and/or the correct 'charge'?"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def atom_num(self):
|
|
115
|
+
return len(self.atoms)
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def coords3d(self):
|
|
119
|
+
return self.coords.reshape(-1, 3)
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def masses(self):
|
|
123
|
+
return self._masses
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def nuc_charges(self):
|
|
127
|
+
return nuc_charges_for_atoms(self.atoms) - self.ecp_electrons
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def mo_num(self):
|
|
131
|
+
return self.C.shape[1]
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def from_file(fn, **kwargs):
|
|
135
|
+
path = Path(fn)
|
|
136
|
+
|
|
137
|
+
from_funcs = {
|
|
138
|
+
".json": Wavefunction.from_orca_json,
|
|
139
|
+
".fchk": Wavefunction.from_fchk,
|
|
140
|
+
}
|
|
141
|
+
from_funcs_for_str = (
|
|
142
|
+
# ORCA
|
|
143
|
+
("Molden file created by orca_2mkl", Wavefunction.from_orca_molden),
|
|
144
|
+
("[AOMix Format", Wavefunction.from_aomix),
|
|
145
|
+
# OpenMolcas
|
|
146
|
+
# ("[N_Atoms]", Wavefunction.from_molden), # seems buggy right now
|
|
147
|
+
)
|
|
148
|
+
try:
|
|
149
|
+
from_func = from_funcs[path.suffix.lower()]
|
|
150
|
+
except KeyError:
|
|
151
|
+
# Search for certain strings in the file
|
|
152
|
+
with open(fn) as handle:
|
|
153
|
+
text = handle.read()
|
|
154
|
+
for key, func in from_funcs_for_str:
|
|
155
|
+
if key in text:
|
|
156
|
+
from_func = func
|
|
157
|
+
break
|
|
158
|
+
else:
|
|
159
|
+
raise Exception("Could not determine file format!")
|
|
160
|
+
return from_func(fn, **kwargs)
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
@file_or_str(".molden", ".molden.input")
|
|
164
|
+
def from_molden(text, **kwargs):
|
|
165
|
+
from pysisyphus.io.molden import wavefunction_from_molden
|
|
166
|
+
|
|
167
|
+
wf = wavefunction_from_molden(text, **kwargs)
|
|
168
|
+
return wf
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
@file_or_str(".json")
|
|
172
|
+
def from_orca_json(text):
|
|
173
|
+
"""Create wavefunction from ORCA JSON.
|
|
174
|
+
|
|
175
|
+
As of version 5.0.3 ORCA does not create JSON files for systems
|
|
176
|
+
containing an ECP, so this method does not take any additional
|
|
177
|
+
args or kwargs in contrast to from_orca_molden."""
|
|
178
|
+
from pysisyphus.io.orca import wavefunction_from_json
|
|
179
|
+
|
|
180
|
+
wf = wavefunction_from_json(text)
|
|
181
|
+
return wf
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
@file_or_str(".molden", ".molden.input")
|
|
185
|
+
def from_orca_molden(text, **kwargs):
|
|
186
|
+
"""Create wavefunction from ORCA molden file.
|
|
187
|
+
|
|
188
|
+
While ORCA refuses to create JSON files for systems containing
|
|
189
|
+
ECPs, this is not the case for 'orca_2mkl [fn] -molden'. So we may
|
|
190
|
+
encounter ECPs here. To handle this 'wavefunction_from_molden' accepts
|
|
191
|
+
an additional charge argument, to specify the correct charge, e.g. as
|
|
192
|
+
stored in an ORCA calculator. If the actual, true charge is not availble
|
|
193
|
+
wavefunction_from_molden will come up with an absurdly high charge.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
from pysisyphus.io.orca import wavefunction_from_molden
|
|
197
|
+
|
|
198
|
+
wf = wavefunction_from_molden(text, **kwargs)
|
|
199
|
+
return wf
|
|
200
|
+
|
|
201
|
+
@staticmethod
|
|
202
|
+
@file_or_str(".molden", ".molden.input")
|
|
203
|
+
def from_fchk(text, **kwargs):
|
|
204
|
+
from pysisyphus.io.fchk import wavefunction_from_fchk
|
|
205
|
+
|
|
206
|
+
wf = wavefunction_from_fchk(text, **kwargs)
|
|
207
|
+
return wf
|
|
208
|
+
|
|
209
|
+
@staticmethod
|
|
210
|
+
@file_or_str(".in")
|
|
211
|
+
def from_aomix(text, **kwargs):
|
|
212
|
+
from pysisyphus.io.aomix import wavefunction_from_aomix
|
|
213
|
+
|
|
214
|
+
wf = wavefunction_from_aomix(text, **kwargs)
|
|
215
|
+
return wf
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def C_occ(self):
|
|
219
|
+
occ_a, occ_b = self.occ
|
|
220
|
+
C_a, C_b = self.C
|
|
221
|
+
return C_a[:, :occ_a], C_b[:, :occ_b]
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def C_virt(self):
|
|
225
|
+
occ_a, occ_b = self.occ
|
|
226
|
+
C_a, C_b = self.C
|
|
227
|
+
return C_a[:, occ_a:], C_b[:, occ_b:]
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def P(self):
|
|
231
|
+
return [C_occ @ C_occ.T for C_occ in self.C_occ]
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def P_tot(self):
|
|
235
|
+
return operator.add(*self.P)
|
|
236
|
+
|
|
237
|
+
def P_exc(self, trans_dens):
|
|
238
|
+
"""
|
|
239
|
+
Eqs. (2.25) and (2.26) in [1].
|
|
240
|
+
"""
|
|
241
|
+
trans_dens *= 2**0.5 / 2
|
|
242
|
+
occ_a, occ_b = self.occ
|
|
243
|
+
assert occ_a == occ_b
|
|
244
|
+
occ = occ_a
|
|
245
|
+
occupations = np.zeros(self.mo_num)
|
|
246
|
+
occupations[:occ_a] = 2
|
|
247
|
+
if self.unrestricted:
|
|
248
|
+
raise Exception("Fix density matrix construction!")
|
|
249
|
+
P = np.diag(occupations)
|
|
250
|
+
dP_oo = np.einsum("ia,ja->ij", trans_dens, trans_dens)
|
|
251
|
+
dP_vv = np.einsum("ia,ic->ac", trans_dens, trans_dens)
|
|
252
|
+
P[:occ, :occ] -= 2 * dP_oo
|
|
253
|
+
P[occ:, occ:] += 2 * dP_vv
|
|
254
|
+
C, _ = self.C
|
|
255
|
+
# The density matric is currently still in the MO basis. Transform
|
|
256
|
+
# it to the AO basis and return.
|
|
257
|
+
return C @ P @ C.T
|
|
258
|
+
|
|
259
|
+
def store_density(self, P, name, ao_or_mo="ao"):
|
|
260
|
+
assert P.ndim == 2, "Handling of alpha/beta densities is not yet implemented!"
|
|
261
|
+
if ao_or_mo == "mo":
|
|
262
|
+
C, _ = self.C
|
|
263
|
+
P_ao = C @ P @ C.T
|
|
264
|
+
elif ao_or_mo == "ao":
|
|
265
|
+
P_ao = P.copy()
|
|
266
|
+
self._densities[name] = P_ao
|
|
267
|
+
|
|
268
|
+
def get_density(self, name):
|
|
269
|
+
return self._densities[name]
|
|
270
|
+
|
|
271
|
+
def eval_density(self, coords3d, P=None):
|
|
272
|
+
if P is None:
|
|
273
|
+
P = self.P_tot
|
|
274
|
+
spherical = self.bf_type == BFType.PURE_SPHERICAL
|
|
275
|
+
vals = self.shells.eval(coords3d, spherical=spherical)
|
|
276
|
+
rho = np.einsum(
|
|
277
|
+
"uv,iu,iv->i", P, vals, vals, optimize=["einsum_path", (0, 1), (0, 1)]
|
|
278
|
+
)
|
|
279
|
+
return rho
|
|
280
|
+
|
|
281
|
+
def eval_esp(self, coords3d, with_nuc=True):
|
|
282
|
+
charges = np.ones(1) # Assume positive charge of 1 e
|
|
283
|
+
esp = np.zeros(len(coords3d))
|
|
284
|
+
nuc_coords3d = self.coords3d
|
|
285
|
+
Pa, Pb = self.P
|
|
286
|
+
for i, c3d in enumerate(coords3d):
|
|
287
|
+
V = self.get_V(c3d[None, :], charges) # 1el Coulomb integrals
|
|
288
|
+
el = np.einsum("uv,uv->", Pa, V)
|
|
289
|
+
if self.unrestricted:
|
|
290
|
+
el += np.einsum("uv,uv->", Pb, V)
|
|
291
|
+
else:
|
|
292
|
+
el *= 2.0
|
|
293
|
+
if with_nuc:
|
|
294
|
+
nuc = (
|
|
295
|
+
self.nuc_charges
|
|
296
|
+
/ np.linalg.norm(nuc_coords3d - c3d[None, :], axis=1)
|
|
297
|
+
).sum()
|
|
298
|
+
else:
|
|
299
|
+
nuc = 0.0
|
|
300
|
+
esp[i] = el + nuc
|
|
301
|
+
return esp
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def ao_centers(self) -> List[int]:
|
|
305
|
+
return list(
|
|
306
|
+
{
|
|
307
|
+
BFType.CARTESIAN: lambda: self.shells.cart_ao_centers,
|
|
308
|
+
BFType.PURE_SPHERICAL: lambda: self.shells.sph_ao_centers,
|
|
309
|
+
}[self.bf_type]()
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def ao_center_map(self) -> Dict[int, List[int]]:
|
|
314
|
+
ao_center_map = dict()
|
|
315
|
+
for i, aoc in enumerate(self.ao_centers):
|
|
316
|
+
ao_center_map.setdefault(aoc, list()).append(i)
|
|
317
|
+
return ao_center_map
|
|
318
|
+
|
|
319
|
+
def as_geom(self, **kwargs):
|
|
320
|
+
return Geometry(self.atoms, self.coords, **kwargs)
|
|
321
|
+
|
|
322
|
+
@property
|
|
323
|
+
def is_cartesian(self) -> bool:
|
|
324
|
+
return self.bf_type == BFType.CARTESIAN
|
|
325
|
+
|
|
326
|
+
#####################
|
|
327
|
+
# Overlap Integrals #
|
|
328
|
+
#####################
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def S(self):
|
|
332
|
+
# Check what type of basis functions we are using.
|
|
333
|
+
return {
|
|
334
|
+
BFType.CARTESIAN: lambda: self.shells.S_cart,
|
|
335
|
+
BFType.PURE_SPHERICAL: lambda: self.shells.S_sph,
|
|
336
|
+
}[self.bf_type]()
|
|
337
|
+
|
|
338
|
+
def S_with_shells(self, other_shells):
|
|
339
|
+
return {
|
|
340
|
+
BFType.CARTESIAN: lambda: self.shells.get_S_cart(other_shells),
|
|
341
|
+
BFType.PURE_SPHERICAL: lambda: self.shells.get_S_sph(other_shells),
|
|
342
|
+
}[self.bf_type]()
|
|
343
|
+
|
|
344
|
+
def S_with(self, other):
|
|
345
|
+
other_shells = other.shells
|
|
346
|
+
return self.S_with_shells(other_shells)
|
|
347
|
+
|
|
348
|
+
def S_MO_with(self, other):
|
|
349
|
+
assert (not self.unrestricted) and (
|
|
350
|
+
self.mult == 1
|
|
351
|
+
), "Currently only Cα is considered!"
|
|
352
|
+
S_AO = self.S_with(other)
|
|
353
|
+
C = self.C[0]
|
|
354
|
+
C_other = other.C[0]
|
|
355
|
+
return C.T @ S_AO @ C_other
|
|
356
|
+
|
|
357
|
+
#####################
|
|
358
|
+
# Multipole Moments #
|
|
359
|
+
#####################
|
|
360
|
+
|
|
361
|
+
def get_origin(self, kind="coc"):
|
|
362
|
+
kind = kind.lower()
|
|
363
|
+
assert kind in ("com", "coc")
|
|
364
|
+
|
|
365
|
+
coords3d = self.coords.reshape(-1, 3).copy()
|
|
366
|
+
_factors = {
|
|
367
|
+
"coc": self.nuc_charges, # Center of charge
|
|
368
|
+
"com": self.masses, # Center of mass
|
|
369
|
+
}
|
|
370
|
+
factors = _factors[kind]
|
|
371
|
+
tot_factors = factors.sum()
|
|
372
|
+
|
|
373
|
+
return np.sum(coords3d * factors[:, None], axis=0) / tot_factors
|
|
374
|
+
|
|
375
|
+
def dipole_ints(
|
|
376
|
+
self, origin: Optional[NDArray] = None, kind: Center = "coc"
|
|
377
|
+
) -> NDArray[float]:
|
|
378
|
+
if origin is None:
|
|
379
|
+
origin = self.get_origin(kind=kind)
|
|
380
|
+
|
|
381
|
+
dipole_ints = {
|
|
382
|
+
BFType.CARTESIAN: lambda *args: self.shells.get_dipole_ints_cart(*args),
|
|
383
|
+
BFType.PURE_SPHERICAL: lambda *args: self.shells.get_dipole_ints_sph(*args),
|
|
384
|
+
}[self.bf_type](origin)
|
|
385
|
+
return dipole_ints
|
|
386
|
+
|
|
387
|
+
def quadrupole_ints(
|
|
388
|
+
self, origin: Optional[NDArray] = None, kind: Center = "coc"
|
|
389
|
+
) -> NDArray[float]:
|
|
390
|
+
if origin is None:
|
|
391
|
+
origin = self.get_origin(kind=kind)
|
|
392
|
+
|
|
393
|
+
quadrupole_ints = {
|
|
394
|
+
BFType.CARTESIAN: lambda *args: self.shells.get_quadrupole_ints_cart(*args),
|
|
395
|
+
BFType.PURE_SPHERICAL: lambda *args: self.shells.get_quadrupole_ints_sph(
|
|
396
|
+
*args
|
|
397
|
+
),
|
|
398
|
+
}[self.bf_type](origin)
|
|
399
|
+
return quadrupole_ints
|
|
400
|
+
|
|
401
|
+
def get_dipole_moment(
|
|
402
|
+
self,
|
|
403
|
+
P: Optional[NDArray[float]] = None,
|
|
404
|
+
origin: Optional[NDArray[float]] = None,
|
|
405
|
+
kind: Center = "coc",
|
|
406
|
+
) -> NDArray[float]:
|
|
407
|
+
if origin is None:
|
|
408
|
+
origin = self.get_origin(kind=kind)
|
|
409
|
+
if P is None:
|
|
410
|
+
P = self.P_tot
|
|
411
|
+
dipole_ints = self.dipole_ints(origin)
|
|
412
|
+
return get_multipole_moment(
|
|
413
|
+
1, self.coords3d, origin, dipole_ints, self.nuc_charges, P
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
@property
|
|
417
|
+
def dipole_moment(self) -> NDArray[float]:
|
|
418
|
+
return self.get_dipole_moment()
|
|
419
|
+
|
|
420
|
+
def get_quadrupole_moment(
|
|
421
|
+
self,
|
|
422
|
+
P: Optional[NDArray[float]] = None,
|
|
423
|
+
origin: Optional[NDArray[float]] = None,
|
|
424
|
+
kind: Center = "coc",
|
|
425
|
+
) -> NDArray[float]:
|
|
426
|
+
if origin is None:
|
|
427
|
+
origin = self.get_origin(kind=kind)
|
|
428
|
+
if P is None:
|
|
429
|
+
P = self.P_tot
|
|
430
|
+
quadrupole_ints = self.quadrupole_ints(origin)
|
|
431
|
+
return get_multipole_moment(
|
|
432
|
+
2, self.coords3d, origin, quadrupole_ints, self.nuc_charges, P
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def quadrupole_moment(self) -> NDArray[float]:
|
|
437
|
+
return self.get_quadrupole_moment()
|
|
438
|
+
|
|
439
|
+
################################
|
|
440
|
+
# Transition Multipole Moments #
|
|
441
|
+
################################
|
|
442
|
+
|
|
443
|
+
def get_transition_multipole_moment(
|
|
444
|
+
self,
|
|
445
|
+
order: int,
|
|
446
|
+
T_a: NDArray[float],
|
|
447
|
+
T_b: NDArray[float] = None,
|
|
448
|
+
origin: Optional[NDArray[float]] = None,
|
|
449
|
+
kind: Center = "coc",
|
|
450
|
+
full=False,
|
|
451
|
+
) -> NDArray[float]:
|
|
452
|
+
if origin is None:
|
|
453
|
+
origin = self.get_origin(kind=kind)
|
|
454
|
+
if order == 1:
|
|
455
|
+
multipole_ints = self.dipole_ints(origin)
|
|
456
|
+
elif order == 2:
|
|
457
|
+
multipole_ints = self.quadrupole_ints(origin)
|
|
458
|
+
else:
|
|
459
|
+
raise Exception("Multipoles of order {order} are not implemented!")
|
|
460
|
+
C_a, C_b = self.C
|
|
461
|
+
occ_a, occ_b = self.occ
|
|
462
|
+
return get_transition_multipole_moment(
|
|
463
|
+
multipole_ints,
|
|
464
|
+
C_a,
|
|
465
|
+
C_b,
|
|
466
|
+
T_a,
|
|
467
|
+
T_b,
|
|
468
|
+
occ_a,
|
|
469
|
+
occ_b,
|
|
470
|
+
full=full,
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
def get_transition_dipole_moment(self, *args, **kwargs):
|
|
474
|
+
return self.get_transition_multipole_moment(1, *args, **kwargs)
|
|
475
|
+
|
|
476
|
+
def get_transition_quadrupole_moment(self, *args, **kwargs):
|
|
477
|
+
return self.get_transition_multipole_moment(2, *args, **kwargs)
|
|
478
|
+
|
|
479
|
+
def oscillator_strength(
|
|
480
|
+
self, exc_ens: NDArray[float], trans_moms: NDArray[float]
|
|
481
|
+
) -> NDArray[float]:
|
|
482
|
+
"""Oscillator strength from TDMs and excitation energies."""
|
|
483
|
+
exc_ens = np.atleast_1d(exc_ens)
|
|
484
|
+
if trans_moms.ndim == 1:
|
|
485
|
+
trans_moms = trans_moms[None, :]
|
|
486
|
+
fosc = 2 / 3 * exc_ens * (trans_moms**2).sum(axis=1)
|
|
487
|
+
return fosc
|
|
488
|
+
|
|
489
|
+
#########################
|
|
490
|
+
# 1el Coulomb Integrals #
|
|
491
|
+
#########################
|
|
492
|
+
|
|
493
|
+
def get_V(self, coords3d, charges):
|
|
494
|
+
return {
|
|
495
|
+
BFType.CARTESIAN: self.shells.get_V_cart,
|
|
496
|
+
BFType.PURE_SPHERICAL: self.shells.get_V_sph,
|
|
497
|
+
}[self.bf_type](coords3d, charges)
|
|
498
|
+
|
|
499
|
+
def __str__(self):
|
|
500
|
+
is_restricted = "unrestricted" if self.unrestricted else "restricted"
|
|
501
|
+
return (
|
|
502
|
+
f"Wavefunction({self.atom_num} atoms, charge={self.charge}, {is_restricted}, "
|
|
503
|
+
f"mult={self.mult})"
|
|
504
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger("mwfn")
|
|
5
|
+
logger.setLevel(logging.DEBUG)
|
|
6
|
+
handler = logging.FileHandler("mwfn.log", mode="w", delay=True)
|
|
7
|
+
# fmt_str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
8
|
+
fmt_str = "%(asctime)s %(message)s"
|
|
9
|
+
formatter = logging.Formatter(fmt_str, datefmt='%Y-%m-%d %H:%M:%S')
|
|
10
|
+
handler.setFormatter(formatter)
|
|
11
|
+
logger.addHandler(handler)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import tempfile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import jinja2
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from pysisyphus.config import get_cmd
|
|
9
|
+
from pysisyphus.constants import BOHR2ANG
|
|
10
|
+
from pysisyphus.helpers_pure import interpolate_colors
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
CUBE_ISO_VAL = 0.001
|
|
14
|
+
TPL_BASE = """
|
|
15
|
+
{{ orient }}
|
|
16
|
+
|
|
17
|
+
function _setModelState() {
|
|
18
|
+
|
|
19
|
+
select;
|
|
20
|
+
Spacefill 0.0;
|
|
21
|
+
|
|
22
|
+
frank off;
|
|
23
|
+
font frank 16.0 SansSerif Plain;
|
|
24
|
+
select *;
|
|
25
|
+
set fontScaling false;
|
|
26
|
+
background white
|
|
27
|
+
frank off
|
|
28
|
+
set showhydrogens True;
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
_setModelState;
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
CUBE_TPL = jinja2.Template(
|
|
35
|
+
"load {{ cube_fn }}\n"
|
|
36
|
+
+ TPL_BASE
|
|
37
|
+
+ """isosurface cutoff {{ isoval }} sign {{ colors }} "{{ cube_fn }}"
|
|
38
|
+
color isosurface translucent
|
|
39
|
+
{% if png_fn %}write image pngt "{{ png_fn }}"{% endif %}
|
|
40
|
+
"""
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def call_jmol(spt_str, show=False):
|
|
45
|
+
with tempfile.NamedTemporaryFile("w", suffix=".spt") as spt_handle:
|
|
46
|
+
spt_handle.write(spt_str)
|
|
47
|
+
spt_handle.flush()
|
|
48
|
+
jmol_cmd = [get_cmd("jmol"), "-n", spt_handle.name]
|
|
49
|
+
if show:
|
|
50
|
+
del jmol_cmd[1]
|
|
51
|
+
proc = subprocess.Popen(
|
|
52
|
+
jmol_cmd,
|
|
53
|
+
universal_newlines=True,
|
|
54
|
+
stdout=subprocess.PIPE,
|
|
55
|
+
stderr=subprocess.PIPE,
|
|
56
|
+
)
|
|
57
|
+
proc.wait()
|
|
58
|
+
stdout = proc.stdout.read()
|
|
59
|
+
stderr = proc.stderr.read()
|
|
60
|
+
return stdout, stderr
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def view_cdd_cube(cdd_cube, isoval=CUBE_ISO_VAL, orient=""):
|
|
64
|
+
spt = CUBE_TPL.render(
|
|
65
|
+
orient=orient,
|
|
66
|
+
cube_fn=cdd_cube,
|
|
67
|
+
isoval=isoval,
|
|
68
|
+
colors="red blue",
|
|
69
|
+
)
|
|
70
|
+
with open("jmol.spt", "w") as handle:
|
|
71
|
+
handle.write(spt)
|
|
72
|
+
stdout, stderr = call_jmol(spt, show=True)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def render_cdd_cube(cdd_cube, isoval=CUBE_ISO_VAL, orient=""):
|
|
76
|
+
png_fn = Path(cdd_cube).with_suffix(".png")
|
|
77
|
+
spt = CUBE_TPL.render(
|
|
78
|
+
orient=orient,
|
|
79
|
+
cube_fn=cdd_cube,
|
|
80
|
+
isoval=isoval,
|
|
81
|
+
colors="red blue",
|
|
82
|
+
png_fn=png_fn,
|
|
83
|
+
)
|
|
84
|
+
with open("jmol.spt", "w") as handle:
|
|
85
|
+
handle.write(spt)
|
|
86
|
+
stdout, stderr = call_jmol(spt)
|
|
87
|
+
return png_fn
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def render_geom_and_charges(geom, point_charges):
|
|
91
|
+
point_charges = point_charges.copy()
|
|
92
|
+
point_charges[:, :3] *= BOHR2ANG
|
|
93
|
+
charges = point_charges[:, -1]
|
|
94
|
+
|
|
95
|
+
cr = np.array((255, 0, 0)) # red
|
|
96
|
+
cw = np.array((255, 255, 255)) # white
|
|
97
|
+
cb = np.array((0, 0, 255)) # blue
|
|
98
|
+
chrg_min = charges.min()
|
|
99
|
+
chrg_max = charges.max()
|
|
100
|
+
print(
|
|
101
|
+
f"charges:\n{np.array2string(point_charges, precision=4)}\n"
|
|
102
|
+
f"min(charges): {chrg_min: .4f}\nmax(charges): {chrg_max: .4f}\n"
|
|
103
|
+
)
|
|
104
|
+
c1 = cb if chrg_min < 0.0 else cw
|
|
105
|
+
c2 = cr if chrg_max > 0.0 else cw
|
|
106
|
+
rgb_colors, _ = interpolate_colors(charges, c1, c2)
|
|
107
|
+
|
|
108
|
+
# Dump geometry to temporary file
|
|
109
|
+
with tempfile.NamedTemporaryFile("w", suffix=".xyz") as tmp_xyz:
|
|
110
|
+
tmp_xyz.write(geom.as_xyz())
|
|
111
|
+
tmp_xyz.flush()
|
|
112
|
+
spt = f"load {tmp_xyz.name};\n"
|
|
113
|
+
for i, ((x, y, z, pc), (r, g, b)) in enumerate(zip(point_charges, rgb_colors)):
|
|
114
|
+
id_ = f"chrg{i}"
|
|
115
|
+
spt += (
|
|
116
|
+
f"isosurface {id_} center {{{x} {y} {z}}} sphere 0.25;\n"
|
|
117
|
+
f"color ${id_} [{r} {g} {b}];\n"
|
|
118
|
+
)
|
|
119
|
+
print(f"SPT:\n\n{spt}")
|
|
120
|
+
call_jmol(spt, show=True)
|