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