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,965 @@
|
|
|
1
|
+
from math import sqrt
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import re
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
from jinja2 import Template
|
|
10
|
+
import numpy as np
|
|
11
|
+
import pyparsing as pp
|
|
12
|
+
|
|
13
|
+
from pysisyphus.calculators.cosmo_data import COSMO_RADII
|
|
14
|
+
from pysisyphus.calculators.OverlapCalculator import OverlapCalculator
|
|
15
|
+
from pysisyphus.calculators.parser import (
|
|
16
|
+
parse_turbo_gradient,
|
|
17
|
+
parse_turbo_ccre0_ascii,
|
|
18
|
+
parse_turbo_mos,
|
|
19
|
+
# parse_turbo_exstates,
|
|
20
|
+
parse_turbo_exstates_re as parse_turbo_exstates,
|
|
21
|
+
)
|
|
22
|
+
from pysisyphus.helpers_pure import file_or_str, get_random_path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def index_strs_for_atoms(atoms):
|
|
26
|
+
indices = dict()
|
|
27
|
+
for i, atom in enumerate(atoms, 1):
|
|
28
|
+
indices.setdefault(atom.lower(), list()).append(i)
|
|
29
|
+
|
|
30
|
+
pairs = dict()
|
|
31
|
+
for key, values in indices.items():
|
|
32
|
+
pairs[key] = list()
|
|
33
|
+
i_end = len(values) - 1
|
|
34
|
+
start = None
|
|
35
|
+
for i, v in enumerate(values):
|
|
36
|
+
if start is None:
|
|
37
|
+
start = v
|
|
38
|
+
if (i == i_end) or (v + 1 != values[i + 1]):
|
|
39
|
+
end = None if (start == v) else v
|
|
40
|
+
pairs[key].append((start, end))
|
|
41
|
+
start = None
|
|
42
|
+
|
|
43
|
+
atom_strs = dict()
|
|
44
|
+
for key, prs in pairs.items():
|
|
45
|
+
tmp = list()
|
|
46
|
+
for start, end in prs:
|
|
47
|
+
if end is None:
|
|
48
|
+
itm = str(start)
|
|
49
|
+
else:
|
|
50
|
+
itm = f"{start}-{end}"
|
|
51
|
+
tmp.append(itm)
|
|
52
|
+
atom_str = f"{key} " + ",".join(tmp)
|
|
53
|
+
atom_strs[key] = atom_str
|
|
54
|
+
|
|
55
|
+
return atom_strs
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_cosmo_data_groups(atoms, epsilon, rsolv=None, dcosmo_rs=None):
|
|
59
|
+
cosmo_dgs = {
|
|
60
|
+
"cosmo": {
|
|
61
|
+
"epsilon": epsilon,
|
|
62
|
+
},
|
|
63
|
+
"cosmo_out": "file=out.ccf",
|
|
64
|
+
"cosmo_data": "file=cosmo_transfer.tmp",
|
|
65
|
+
}
|
|
66
|
+
if rsolv is not None:
|
|
67
|
+
cosmo_dgs["cosmo"]["rsolv"] = rsolv
|
|
68
|
+
# Transform atom list into TURBOMOLE-style compressed indices
|
|
69
|
+
# H, H, H, O, O, H, O
|
|
70
|
+
# is transformed to
|
|
71
|
+
# h 1-3, 6
|
|
72
|
+
# o 4-5, 7
|
|
73
|
+
# etc.
|
|
74
|
+
index_strs = index_strs_for_atoms(atoms)
|
|
75
|
+
cosmo_atom_strs = list()
|
|
76
|
+
cosmo_atoms = dict()
|
|
77
|
+
# This does not yet work; so we stick with the default radii.
|
|
78
|
+
# for key, index_str in index_strs.items():
|
|
79
|
+
# radius = COSMO_RADII[key].radius
|
|
80
|
+
# cosmo_atoms[index_str] = f"\nradius={radius:.6f}"
|
|
81
|
+
# cosmo_dgs["cosmo_atoms"] = cosmo_atoms
|
|
82
|
+
|
|
83
|
+
if dcosmo_rs:
|
|
84
|
+
cosmo_dgs["dcosmo_rs"] = f"file={dcosmo_rs}"
|
|
85
|
+
return cosmo_dgs
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
DATA_GROUP_TPL = Template(
|
|
89
|
+
"""
|
|
90
|
+
{% for dg in data_groups %}
|
|
91
|
+
${{ dg }}
|
|
92
|
+
{% for key, value in data_groups[dg].items() %}
|
|
93
|
+
{{ key }}{% if value %} ={{ value }}{% endif %}
|
|
94
|
+
|
|
95
|
+
{% endfor %}
|
|
96
|
+
{% endfor %}
|
|
97
|
+
""",
|
|
98
|
+
trim_blocks=True,
|
|
99
|
+
lstrip_blocks=True,
|
|
100
|
+
)
|
|
101
|
+
SIMPLE_INPUT_TPL = Template(
|
|
102
|
+
"""
|
|
103
|
+
$atoms
|
|
104
|
+
basis={{ basis }}
|
|
105
|
+
$coord file=coord
|
|
106
|
+
$grad file=gradient
|
|
107
|
+
$symmetry c1
|
|
108
|
+
$eht charge={{ charge }} unpaired={{ unpaired }}
|
|
109
|
+
{{ data_groups }}
|
|
110
|
+
$end
|
|
111
|
+
"""
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def render_data_groups(raw_data_groups):
|
|
116
|
+
data_groups = dict()
|
|
117
|
+
for dg, kws in raw_data_groups.items():
|
|
118
|
+
# datagroup w/o keywords
|
|
119
|
+
if kws is None:
|
|
120
|
+
data_groups[dg] = dict()
|
|
121
|
+
# datagroup w/ one keyword on same line. Here we just merge the name and the keyword,
|
|
122
|
+
# so they appear on the same line.
|
|
123
|
+
elif type(kws) == str:
|
|
124
|
+
data_groups[f"{dg} {kws}"] = dict()
|
|
125
|
+
# Otherwise a dict is expected
|
|
126
|
+
elif type(kws) == dict:
|
|
127
|
+
data_groups[dg] = kws
|
|
128
|
+
else:
|
|
129
|
+
raise Exception(f"Can't handle input '{dg}': '{kws}'!")
|
|
130
|
+
return DATA_GROUP_TPL.render(data_groups=data_groups)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def control_from_simple_input(simple_inp, charge, mult, cosmo_kwargs=None):
|
|
134
|
+
"""Create control file from 'simple input'.
|
|
135
|
+
|
|
136
|
+
See examples/opt/26_turbomole_simple_input/ for an example."""
|
|
137
|
+
unpaired = mult - 1
|
|
138
|
+
simple_inp = simple_inp.copy()
|
|
139
|
+
basis = simple_inp.pop("basis")
|
|
140
|
+
# Add memory statement, if not already present
|
|
141
|
+
if "maxcor" not in simple_inp.keys():
|
|
142
|
+
simple_inp["maxcor"] = "500 MiB per_core"
|
|
143
|
+
|
|
144
|
+
data_groups = render_data_groups(simple_inp)
|
|
145
|
+
|
|
146
|
+
rendered = SIMPLE_INPUT_TPL.render(
|
|
147
|
+
basis=basis,
|
|
148
|
+
charge=charge,
|
|
149
|
+
unpaired=unpaired,
|
|
150
|
+
data_groups=data_groups,
|
|
151
|
+
)
|
|
152
|
+
return rendered
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class Turbomole(OverlapCalculator):
|
|
156
|
+
conf_key = "turbomole"
|
|
157
|
+
_set_plans = (
|
|
158
|
+
"out",
|
|
159
|
+
"control",
|
|
160
|
+
"alpha",
|
|
161
|
+
"beta",
|
|
162
|
+
"mos",
|
|
163
|
+
("ciss_a", "td_vec_fn"),
|
|
164
|
+
("sing_a", "td_vec_fn"),
|
|
165
|
+
"ccres",
|
|
166
|
+
"exstates",
|
|
167
|
+
"mwfn_wf",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def __init__(
|
|
171
|
+
self,
|
|
172
|
+
control_path=None,
|
|
173
|
+
simple_input=None,
|
|
174
|
+
root=None,
|
|
175
|
+
double_mol_path=None,
|
|
176
|
+
cosmo_kwargs=None,
|
|
177
|
+
**kwargs,
|
|
178
|
+
):
|
|
179
|
+
super().__init__(**kwargs)
|
|
180
|
+
|
|
181
|
+
assert (control_path is not None) or (
|
|
182
|
+
simple_input is not None
|
|
183
|
+
), "Please either provide a prepared 'control_path' or 'simple_input'!"
|
|
184
|
+
|
|
185
|
+
# Handle simple input
|
|
186
|
+
if simple_input:
|
|
187
|
+
control_path = (self.out_dir / get_random_path("control_path")).absolute()
|
|
188
|
+
self.log(
|
|
189
|
+
"Set 'control_path' to '{control_path}'. Creating 'control' from simple input in it."
|
|
190
|
+
)
|
|
191
|
+
try:
|
|
192
|
+
control_path.mkdir()
|
|
193
|
+
except FileExistsError:
|
|
194
|
+
# Clean directory if already exists
|
|
195
|
+
for fn in control_path.iterdir():
|
|
196
|
+
fn.unlink()
|
|
197
|
+
control_str = control_from_simple_input(
|
|
198
|
+
simple_input, charge=self.charge, mult=self.mult
|
|
199
|
+
)
|
|
200
|
+
with open(control_path / "control", "w") as handle:
|
|
201
|
+
handle.write(control_str)
|
|
202
|
+
# End of simple input handling
|
|
203
|
+
|
|
204
|
+
# Set provided control_path or use the one generated for simple_input
|
|
205
|
+
self.control_path = Path(control_path).absolute()
|
|
206
|
+
|
|
207
|
+
self.root = root
|
|
208
|
+
self.double_mol_path = double_mol_path
|
|
209
|
+
if self.double_mol_path:
|
|
210
|
+
self.double_mol_path = Path(self.double_mol_path)
|
|
211
|
+
# Check if the overlap matrix will be printed and assert
|
|
212
|
+
# that no SCF iterations are done.
|
|
213
|
+
if self.double_mol_path:
|
|
214
|
+
with open(self.double_mol_path / "control") as handle:
|
|
215
|
+
text = handle.read()
|
|
216
|
+
assert re.search(r"\$intsdebug\s*sao", text) and re.search(
|
|
217
|
+
r"\$scfiterlimit\s*0", text
|
|
218
|
+
), ("Please set " "$intsdebug sao and $scfiterlimit 0 !")
|
|
219
|
+
self.cosmo_kwargs = cosmo_kwargs
|
|
220
|
+
if self.cosmo_kwargs:
|
|
221
|
+
assert (
|
|
222
|
+
"epsilon" in self.cosmo_kwargs
|
|
223
|
+
), "If 'cosmo_kwargs' is given 'epsilon' must be specified!"
|
|
224
|
+
|
|
225
|
+
self.to_keep = (
|
|
226
|
+
"control",
|
|
227
|
+
"mos",
|
|
228
|
+
"alpha",
|
|
229
|
+
"beta",
|
|
230
|
+
"out",
|
|
231
|
+
"ciss_a",
|
|
232
|
+
"ucis_a",
|
|
233
|
+
"gradient",
|
|
234
|
+
"sing_a",
|
|
235
|
+
"__ccre*",
|
|
236
|
+
"exstates",
|
|
237
|
+
"coord",
|
|
238
|
+
"mwfn_wf:wavefunction.molden",
|
|
239
|
+
"input.xyz",
|
|
240
|
+
"pc_gradients",
|
|
241
|
+
"nprhessian",
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
self.parser_funcs = {
|
|
245
|
+
"energy": self.parse_energy,
|
|
246
|
+
"force": self.parse_force,
|
|
247
|
+
"hessian": self.parse_hessian,
|
|
248
|
+
"double_mol": self.parse_double_mol,
|
|
249
|
+
"noparse": lambda path: None,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# Turbomole uses the 'control' file implicitly
|
|
253
|
+
self.inp_fn = ""
|
|
254
|
+
self.out_fn = "turbomole.out"
|
|
255
|
+
# MO coefficient files
|
|
256
|
+
self.mos = None
|
|
257
|
+
self.alpha = None
|
|
258
|
+
self.beta = None
|
|
259
|
+
|
|
260
|
+
# Prepare base_cmd
|
|
261
|
+
with open(self.control_path / "control") as handle:
|
|
262
|
+
text = handle.read()
|
|
263
|
+
scf_cmd = "dscf"
|
|
264
|
+
second_cmd = "grad"
|
|
265
|
+
# Check for RI
|
|
266
|
+
if ("$rij" in text) or ("$rik" in text):
|
|
267
|
+
scf_cmd = "ridft"
|
|
268
|
+
second_cmd = "rdgrad"
|
|
269
|
+
self.log("Found RI calculation.")
|
|
270
|
+
|
|
271
|
+
self.uhf = ("$uhf" in text) or (self.mult > 1)
|
|
272
|
+
|
|
273
|
+
# It seems as they changed whats written in the control file in version 7.7
|
|
274
|
+
try:
|
|
275
|
+
self.set_occ_and_mo_nums(text)
|
|
276
|
+
except TypeError:
|
|
277
|
+
print(
|
|
278
|
+
"Parsing of occupied and virtual MO numbers failed! Disabling "
|
|
279
|
+
"excited state tracking!"
|
|
280
|
+
)
|
|
281
|
+
self.track = False
|
|
282
|
+
|
|
283
|
+
assert not (("$exopt" in text) and ("$ricc2" in text)), (
|
|
284
|
+
"Found $exopt and $ricc2 in the control file! $exopt is used "
|
|
285
|
+
"for TD-DFT/TDA gradients whereas $ricc2 with 'geoopt ...' "
|
|
286
|
+
"leads to ricc2 gradients. Please delete one of the keywords!"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
self.td = False
|
|
290
|
+
self.td_vec_fn = None
|
|
291
|
+
self.ricc2 = False
|
|
292
|
+
self.ricc2_opt = False
|
|
293
|
+
# Check for excited state calculation
|
|
294
|
+
if "$exopt" in text:
|
|
295
|
+
exopt_re = r"\$exopt\s*(\d+)"
|
|
296
|
+
self.root = int(re.search(exopt_re, text)[1])
|
|
297
|
+
second_cmd = "egrad"
|
|
298
|
+
self.prepare_td(text)
|
|
299
|
+
self.td = True
|
|
300
|
+
elif "$soes" in text:
|
|
301
|
+
second_cmd = "escf"
|
|
302
|
+
self.td = True
|
|
303
|
+
self.prepare_td(text)
|
|
304
|
+
elif ("$ricc2" in text) and ("$excitations" in text):
|
|
305
|
+
self.ricc2 = True
|
|
306
|
+
self.ricc2_opt = "geoopt" in text
|
|
307
|
+
second_cmd = "ricc2"
|
|
308
|
+
self.prepare_td(text)
|
|
309
|
+
self.root = self.get_ricc2_root(text)
|
|
310
|
+
try:
|
|
311
|
+
frozen_mos = int(re.search(r"implicit core=\s*(\d+)", text)[1])
|
|
312
|
+
except TypeError:
|
|
313
|
+
frozen_mos = 0
|
|
314
|
+
self.frozen_mos = frozen_mos
|
|
315
|
+
self.log(f"Found {self.frozen_mos} frozen orbitals.")
|
|
316
|
+
if self.track:
|
|
317
|
+
assert self.td or self.ricc2, (
|
|
318
|
+
"track=True can only be used "
|
|
319
|
+
"in connection with excited state calculations."
|
|
320
|
+
)
|
|
321
|
+
# Right now this can't handle a root flip from some excited state
|
|
322
|
+
# to the ground state ... Then we would need grad/rdgrad again,
|
|
323
|
+
# instead of egrad.
|
|
324
|
+
self.scf_cmd = scf_cmd
|
|
325
|
+
self.second_cmd = second_cmd
|
|
326
|
+
|
|
327
|
+
# Setup several cmds, depending on the calc type
|
|
328
|
+
def get_cmd(cmd):
|
|
329
|
+
return ";".join((self.scf_cmd, cmd))
|
|
330
|
+
|
|
331
|
+
if self.td:
|
|
332
|
+
self.energy_cmd = get_cmd("escf")
|
|
333
|
+
self.forces_cmd = get_cmd("egrad")
|
|
334
|
+
self.hessian_cmd = "not_yet_implemented"
|
|
335
|
+
elif self.ricc2:
|
|
336
|
+
ricc2_cmd = get_cmd("ricc2")
|
|
337
|
+
self.energy_cmd = ricc2_cmd
|
|
338
|
+
self.forces_cmd = ricc2_cmd
|
|
339
|
+
self.hessian_cmd = "not_yet_implemented"
|
|
340
|
+
else:
|
|
341
|
+
self.energy_cmd = self.scf_cmd
|
|
342
|
+
self.forces_cmd = get_cmd(second_cmd)
|
|
343
|
+
self.hessian_cmd = get_cmd("aoforce")
|
|
344
|
+
self.log("Prepared commands:")
|
|
345
|
+
self.log("\tEnergy cmd: " + self.energy_cmd)
|
|
346
|
+
self.log("\tForces cmd: " + self.forces_cmd)
|
|
347
|
+
self.log("\tHessian cmd: " + self.hessian_cmd)
|
|
348
|
+
|
|
349
|
+
if self.td or self.ricc2 and (self.root is None):
|
|
350
|
+
self.root = 1
|
|
351
|
+
warnings.warn(
|
|
352
|
+
"No root set! Either include '$exopt' for TDA/TDDFT or "
|
|
353
|
+
"'geoopt' for ricc2 in the control or supply a value for 'root'! "
|
|
354
|
+
f"Continuing with root={self.root}."
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
def set_occ_and_mo_nums(self, text):
|
|
358
|
+
# Determine number of basis functions
|
|
359
|
+
nbf_re = r"nbf\(AO\)=(\d+)"
|
|
360
|
+
nbf = int(re.search(nbf_re, text)[1])
|
|
361
|
+
|
|
362
|
+
self.occ_mos = None
|
|
363
|
+
self.virt_mos = None
|
|
364
|
+
|
|
365
|
+
# Determine number of occupied orbitals
|
|
366
|
+
if not self.uhf:
|
|
367
|
+
# Sometimes Turbomole does NOT use a range, but only a single integer,
|
|
368
|
+
# e.g., for hydrogen (H_2).
|
|
369
|
+
occ_re = r"closed shells\s+(\w)\s*(\d+)-?(\d*)"
|
|
370
|
+
occ_mobj = re.search(occ_re, text)
|
|
371
|
+
occ_mos = occ_mobj[3]
|
|
372
|
+
if occ_mos == "":
|
|
373
|
+
occ_mos = occ_mobj[2]
|
|
374
|
+
self.occ_mos = int(occ_mos)
|
|
375
|
+
|
|
376
|
+
self.log(f"Found {self.occ_mos} occupied MOs.")
|
|
377
|
+
# Number of spherical basis functions. May be different from CAO
|
|
378
|
+
# Determine number of virtual orbitals
|
|
379
|
+
self.virt_mos = nbf - self.occ_mos
|
|
380
|
+
else:
|
|
381
|
+
alpha_re = r"alpha shells\s+(\w)\s*\d+-(\d+)"
|
|
382
|
+
alpha_mos = int(re.search(alpha_re, text)[2])
|
|
383
|
+
self.log(f"Found {alpha_mos} occupied alpha MOs.")
|
|
384
|
+
|
|
385
|
+
beta_re = r"beta shells\s+(\w)\s*\d+-(\d+)"
|
|
386
|
+
beta_mos = int(re.search(beta_re, text)[2])
|
|
387
|
+
self.log(f"Found {beta_mos} occupied beta MOs.")
|
|
388
|
+
|
|
389
|
+
def get_ricc2_root(self, text):
|
|
390
|
+
regex = r"geoopt.+?state=\((.+?)\)"
|
|
391
|
+
mobj = re.search(regex, text)
|
|
392
|
+
if not mobj:
|
|
393
|
+
root = None
|
|
394
|
+
elif mobj[1] == "x":
|
|
395
|
+
root = 0
|
|
396
|
+
else:
|
|
397
|
+
assert mobj[1].startswith("a "), "symmetry isn't supported!"
|
|
398
|
+
root = int(mobj[1][-1])
|
|
399
|
+
return root
|
|
400
|
+
|
|
401
|
+
def prepare_td(self, text):
|
|
402
|
+
self.log("Preparing for excited state (gradient) calculations")
|
|
403
|
+
self.td_vec_fn = None
|
|
404
|
+
self.ci_coeffs = None
|
|
405
|
+
self.mo_inds = None
|
|
406
|
+
|
|
407
|
+
def prepare_point_charges(self, point_charges):
|
|
408
|
+
"""$point_charges
|
|
409
|
+
<x> <y> <z> <q>
|
|
410
|
+
"""
|
|
411
|
+
lines = [f"{x:.12} {y:.12f} {z:.12f} {q:.12f}" for x, y, z, q in point_charges]
|
|
412
|
+
|
|
413
|
+
return "$point_charges\n\t" + "\n".join(lines)
|
|
414
|
+
|
|
415
|
+
def prepare_input(self, atoms, coords, calc_type, point_charges=None):
|
|
416
|
+
"""To rectify this we have to construct the basecmd
|
|
417
|
+
dynamically and construct it ad hoc. We could set a RI flag
|
|
418
|
+
in the beginning and select the correct scf binary here from
|
|
419
|
+
it. Then we select the following binary on demand, e.g. aoforce
|
|
420
|
+
or rdgrad or egrad etc."""
|
|
421
|
+
|
|
422
|
+
valid_calc_types = ("energy", "force", "double_mol", "noparse", "hessian")
|
|
423
|
+
if calc_type not in valid_calc_types:
|
|
424
|
+
raise Exception(
|
|
425
|
+
f"Invalid calc_type '{calc_type}'! Supported "
|
|
426
|
+
f"calc_types are '{valid_calc_types}'."
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
path = self.prepare_path(use_in_run=True)
|
|
430
|
+
if calc_type == "double_mol":
|
|
431
|
+
copy_from = self.double_mol_path
|
|
432
|
+
else:
|
|
433
|
+
copy_from = self.control_path
|
|
434
|
+
# Copy everything from the reference control_dir into this path
|
|
435
|
+
# Use self.control_path for all calculations except the double
|
|
436
|
+
# molecule calculation.
|
|
437
|
+
"""Maybe we shouldn't copy everything because it may give convergence
|
|
438
|
+
problems? Right now we use the initial MO guess generated in the
|
|
439
|
+
reference path for all images along the path."""
|
|
440
|
+
for glob in copy_from.glob("./*"):
|
|
441
|
+
shutil.copy(glob, path)
|
|
442
|
+
xyz_str = self.prepare_xyz_string(atoms, coords)
|
|
443
|
+
with open(path / "input.xyz", "w") as handle:
|
|
444
|
+
handle.write(xyz_str)
|
|
445
|
+
# Write coordinates
|
|
446
|
+
coord_str = self.prepare_turbo_coords(atoms, coords)
|
|
447
|
+
coord_fn = path / "coord"
|
|
448
|
+
with open(coord_fn, "w") as handle:
|
|
449
|
+
handle.write(coord_str)
|
|
450
|
+
# Copy MO coefficients from previous cycle with this calculator
|
|
451
|
+
# if present.
|
|
452
|
+
if self.mos:
|
|
453
|
+
shutil.copy(self.mos, path / "mos")
|
|
454
|
+
self.log(f"Using {self.mos} as MO guess.")
|
|
455
|
+
elif self.alpha and self.beta:
|
|
456
|
+
shutil.copy(self.alpha, path / "alpha")
|
|
457
|
+
shutil.copy(self.beta, path / "beta")
|
|
458
|
+
self.log(f"Using {self.alpha} and {self.beta} as MO guesses.")
|
|
459
|
+
if self.td_vec_fn:
|
|
460
|
+
# The suffix contains the true name with a leading
|
|
461
|
+
# dot, that we drop.
|
|
462
|
+
td_vec_fn = self.td_vec_fn.suffix[1:]
|
|
463
|
+
shutil.copy(self.td_vec_fn, path / td_vec_fn)
|
|
464
|
+
self.log(f"Using '{self.td_vec_fn}' as escf guess.")
|
|
465
|
+
|
|
466
|
+
# Set memory
|
|
467
|
+
self.sub_control(r"\$maxcor.+", f"$maxcor {self.mem} MiB per_core")
|
|
468
|
+
|
|
469
|
+
if self.cosmo_kwargs is not None:
|
|
470
|
+
cosmo_data_groups = get_cosmo_data_groups(**self.cosmo_kwargs, atoms=atoms)
|
|
471
|
+
cosmo_rendered = render_data_groups(cosmo_data_groups)
|
|
472
|
+
self.sub_control(r"\$end", cosmo_rendered + "\n$end")
|
|
473
|
+
|
|
474
|
+
root_log_msg = f"with current root information: {self.root}"
|
|
475
|
+
if self.root and self.td:
|
|
476
|
+
repl = f"$exopt {self.root}"
|
|
477
|
+
self.sub_control(r"\$exopt\s*(\d+)", f"$exopt {self.root}", root_log_msg)
|
|
478
|
+
self.log(f"Using '{repl}'")
|
|
479
|
+
|
|
480
|
+
if self.root and self.ricc2:
|
|
481
|
+
repl = f"state=(a {self.root})"
|
|
482
|
+
self.sub_control(
|
|
483
|
+
r"state=\(a\s+(?P<state>\d+)\)", f"state=(a {self.root})", root_log_msg
|
|
484
|
+
)
|
|
485
|
+
self.log(f"Using '{repl}' for geoopt.")
|
|
486
|
+
|
|
487
|
+
if point_charges is not None:
|
|
488
|
+
charge_num = len(point_charges)
|
|
489
|
+
pc_str = self.prepare_point_charges(point_charges)
|
|
490
|
+
self.sub_control(
|
|
491
|
+
r"\$end", pc_str + "\n$end", f"appended {charge_num} point charges"
|
|
492
|
+
)
|
|
493
|
+
# Activate calculation of gradients on point charges
|
|
494
|
+
self.sub_control(r"\$drvopt", "$drvopt\npoint charges\n")
|
|
495
|
+
# Write point charge gradients to file
|
|
496
|
+
self.sub_control(
|
|
497
|
+
r"\$end", "$point_charge_gradients file=pc_gradients\n$end"
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
if calc_type == "hessian":
|
|
501
|
+
self.append_control("$noproj\n$nprhessian file=nprhessian")
|
|
502
|
+
|
|
503
|
+
def sub_control(self, pattern, repl, log_msg="", **kwargs):
|
|
504
|
+
path = self.path_already_prepared
|
|
505
|
+
assert path
|
|
506
|
+
self.log(f"Updating control file in '{path}' {log_msg}")
|
|
507
|
+
control_path = path / "control"
|
|
508
|
+
with open(control_path) as handle:
|
|
509
|
+
text = handle.read()
|
|
510
|
+
text = re.sub(pattern, repl, text, **kwargs)
|
|
511
|
+
with open(control_path, "w") as handle:
|
|
512
|
+
handle.write(text)
|
|
513
|
+
|
|
514
|
+
def append_control(self, to_append, log_msg="", **kwargs):
|
|
515
|
+
self.sub_control(r"\$end", f"{to_append}\n$end", log_msg, **kwargs)
|
|
516
|
+
|
|
517
|
+
def get_pal_env(self):
|
|
518
|
+
env_copy = os.environ.copy()
|
|
519
|
+
env_copy["PARA_ARCH"] = "SMP"
|
|
520
|
+
env_copy["PARNODES"] = str(self.pal)
|
|
521
|
+
env_copy["SMPCPUS"] = str(self.pal)
|
|
522
|
+
|
|
523
|
+
return env_copy
|
|
524
|
+
|
|
525
|
+
def store_and_track(self, results, func, atoms, coords, **prepare_kwargs):
|
|
526
|
+
if self.track:
|
|
527
|
+
prev_run_path = self.last_run_path
|
|
528
|
+
self.store_overlap_data(atoms, coords)
|
|
529
|
+
# Redo the calculation with the updated root
|
|
530
|
+
if self.track_root():
|
|
531
|
+
self.calc_counter += 1
|
|
532
|
+
results = func(atoms, coords, **prepare_kwargs)
|
|
533
|
+
self.last_run_path = prev_run_path
|
|
534
|
+
try:
|
|
535
|
+
shutil.rmtree(self.last_run_path)
|
|
536
|
+
except FileNotFoundError:
|
|
537
|
+
self.log(f"'{self.last_run_path}' has already been deleted!")
|
|
538
|
+
results["all_energies"] = self.parse_all_energies()
|
|
539
|
+
return results
|
|
540
|
+
|
|
541
|
+
def get_energy(self, atoms, coords, **prepare_kwargs):
|
|
542
|
+
self.prepare_input(atoms, coords, "energy", **prepare_kwargs)
|
|
543
|
+
kwargs = {
|
|
544
|
+
"calc": "energy",
|
|
545
|
+
"shell": True,
|
|
546
|
+
"hold": self.track,
|
|
547
|
+
"env": self.get_pal_env(),
|
|
548
|
+
"cmd": self.energy_cmd,
|
|
549
|
+
}
|
|
550
|
+
results = self.run(None, **kwargs)
|
|
551
|
+
results = self.store_and_track(
|
|
552
|
+
results, self.get_energy, atoms, coords, **prepare_kwargs
|
|
553
|
+
)
|
|
554
|
+
return results
|
|
555
|
+
|
|
556
|
+
def get_forces(self, atoms, coords, cmd=None, **prepare_kwargs):
|
|
557
|
+
self.prepare_input(atoms, coords, "force", **prepare_kwargs)
|
|
558
|
+
|
|
559
|
+
if cmd is None:
|
|
560
|
+
cmd = self.forces_cmd
|
|
561
|
+
|
|
562
|
+
kwargs = {
|
|
563
|
+
"calc": "force",
|
|
564
|
+
"shell": True, # To allow chained commands like 'ridft; rdgrad'
|
|
565
|
+
"hold": self.track, # Keep the files for WFOverlap
|
|
566
|
+
"env": self.get_pal_env(),
|
|
567
|
+
"cmd": cmd,
|
|
568
|
+
}
|
|
569
|
+
# Use inp=None because we don't use any dedicated input besides
|
|
570
|
+
# the previously prepared control file and the current coords.
|
|
571
|
+
results = self.run(None, **kwargs)
|
|
572
|
+
results = self.store_and_track(
|
|
573
|
+
results, self.get_forces, atoms, coords, **prepare_kwargs
|
|
574
|
+
)
|
|
575
|
+
return results
|
|
576
|
+
|
|
577
|
+
def get_hessian(self, atoms, coords, **prepare_kwargs):
|
|
578
|
+
if self.td or self.ricc2:
|
|
579
|
+
raise Exception("ricc2 or TD-DFT/TDA hessian not yet supported!")
|
|
580
|
+
|
|
581
|
+
self.prepare_input(atoms, coords, "hessian", **prepare_kwargs)
|
|
582
|
+
kwargs = {
|
|
583
|
+
"calc": "hessian",
|
|
584
|
+
"shell": True, # To allow chained commands like 'ridft; rdgrad'
|
|
585
|
+
"hold": self.track,
|
|
586
|
+
"env": self.get_pal_env(),
|
|
587
|
+
"cmd": self.hessian_cmd,
|
|
588
|
+
}
|
|
589
|
+
results = self.run(None, **kwargs)
|
|
590
|
+
results = self.store_and_track(
|
|
591
|
+
results, self.get_hessian, atoms, coords, **prepare_kwargs
|
|
592
|
+
)
|
|
593
|
+
return results
|
|
594
|
+
|
|
595
|
+
def run_calculation(self, atoms, coords, **prepare_kwargs):
|
|
596
|
+
return self.get_energy(atoms, coords, **prepare_kwargs)
|
|
597
|
+
|
|
598
|
+
def run_double_mol_calculation(self, atoms, coords1, coords2):
|
|
599
|
+
if not self.double_mol_path:
|
|
600
|
+
self.log(
|
|
601
|
+
"Skipping double molecule calculations as double mol "
|
|
602
|
+
"path is not specified.!"
|
|
603
|
+
)
|
|
604
|
+
return None
|
|
605
|
+
self.log("Running double molecule calculation")
|
|
606
|
+
double_atoms = atoms + atoms
|
|
607
|
+
double_coords = np.hstack((coords1, coords2))
|
|
608
|
+
self.prepare_input(double_atoms, double_coords, "double_mol")
|
|
609
|
+
kwargs = {
|
|
610
|
+
"calc": "double_mol",
|
|
611
|
+
"shell": True,
|
|
612
|
+
"keep": False,
|
|
613
|
+
"hold": True,
|
|
614
|
+
"cmd": self.scf_cmd,
|
|
615
|
+
"env": self.get_pal_env(),
|
|
616
|
+
}
|
|
617
|
+
results = self.run(None, **kwargs)
|
|
618
|
+
return results
|
|
619
|
+
|
|
620
|
+
def parse_double_mol(self, path):
|
|
621
|
+
"""Parse a double molecule overlap matrix from Turbomole output
|
|
622
|
+
to be used with WFOWrapper."""
|
|
623
|
+
with open(path / self.out_fn) as handle:
|
|
624
|
+
text = handle.read()
|
|
625
|
+
regex = r"OVERLAP\(SAO\)\s+-+([\d\.E\-\s*\+]+)\s+-+"
|
|
626
|
+
ovlp_str = re.search(regex, text)[1]
|
|
627
|
+
ovlp = np.array(ovlp_str.strip().split(), dtype=np.float64)
|
|
628
|
+
mo_num = self.occ_mos + self.virt_mos
|
|
629
|
+
double_mo_num = 2 * mo_num
|
|
630
|
+
full_ovlp = np.zeros((double_mo_num, double_mo_num))
|
|
631
|
+
full_ovlp[np.tril_indices(double_mo_num)] = ovlp
|
|
632
|
+
double_mol_S = full_ovlp[mo_num:, :mo_num]
|
|
633
|
+
return double_mol_S
|
|
634
|
+
|
|
635
|
+
def parse_mos(self):
|
|
636
|
+
pass
|
|
637
|
+
|
|
638
|
+
def parse_energy(self, path):
|
|
639
|
+
with open(path / self.out_fn) as handle:
|
|
640
|
+
text = handle.read()
|
|
641
|
+
en_regex = re.compile(r"Total energy\s*:?\s*=?\s*([\d\-\.]+)", re.IGNORECASE)
|
|
642
|
+
tot_ens = en_regex.findall(text)
|
|
643
|
+
|
|
644
|
+
if self.td:
|
|
645
|
+
# Drop ground state energy that is repeated
|
|
646
|
+
tot_en = tot_ens[1:][self.root]
|
|
647
|
+
elif self.ricc2 and self.ricc2_opt:
|
|
648
|
+
results = parse_turbo_gradient(path)
|
|
649
|
+
tot_en = results["energy"]
|
|
650
|
+
elif self.ricc2 and not self.ricc2_opt:
|
|
651
|
+
raise Exception("Implement me!")
|
|
652
|
+
else:
|
|
653
|
+
tot_en = tot_ens[0]
|
|
654
|
+
|
|
655
|
+
tot_en = float(tot_en)
|
|
656
|
+
return {
|
|
657
|
+
"energy": tot_en,
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
@staticmethod
|
|
661
|
+
@file_or_str(".sing_a", ".ciss_a") # , exact=True)
|
|
662
|
+
def parse_tddft_tden(text):
|
|
663
|
+
eigval_re = re.compile(r"(\d+)\s+eigenvalue\s+=\s+([\d\.\-D\+]+)")
|
|
664
|
+
eigvals = eigval_re.findall(text)
|
|
665
|
+
state_inds, exc_ens = zip(*eigvals)
|
|
666
|
+
exc_ens = [exc_en.replace("D", "E") for exc_en in exc_ens]
|
|
667
|
+
return np.array(exc_ens, dtype=float)
|
|
668
|
+
|
|
669
|
+
def parse_all_energies(self):
|
|
670
|
+
# Parse eigenvectors from escf/egrad calculation
|
|
671
|
+
gs_energy = self.parse_gs_energy()
|
|
672
|
+
if self.second_cmd in ("escf", "egrad"):
|
|
673
|
+
exc_energies = Turbomole.parse_tddft_tden(self.td_vec_fn)
|
|
674
|
+
# Parse eigenvectors from ricc2 calculation
|
|
675
|
+
elif self.second_cmd == "ricc2":
|
|
676
|
+
with open(self.exstates) as handle:
|
|
677
|
+
exstates_text = handle.read()
|
|
678
|
+
exc_energies_by_model = parse_turbo_exstates(exstates_text)
|
|
679
|
+
# Drop CCS and take energies from whatever model was used
|
|
680
|
+
exc_energies = [
|
|
681
|
+
(model, exc_ens)
|
|
682
|
+
for model, exc_ens in exc_energies_by_model.items()
|
|
683
|
+
if model != "CCS"
|
|
684
|
+
]
|
|
685
|
+
assert len(exc_energies) == 1
|
|
686
|
+
_, exc_energies = exc_energies[0]
|
|
687
|
+
|
|
688
|
+
else:
|
|
689
|
+
exc_energies = np.array(list())
|
|
690
|
+
|
|
691
|
+
if exc_energies.size == 0:
|
|
692
|
+
all_energies = np.array((gs_energy,))
|
|
693
|
+
else:
|
|
694
|
+
all_energies = np.full(len(exc_energies) + 1, gs_energy)
|
|
695
|
+
all_energies[1:] += exc_energies
|
|
696
|
+
|
|
697
|
+
return all_energies
|
|
698
|
+
|
|
699
|
+
def parse_ci_coeffs(self):
|
|
700
|
+
if self.second_cmd in ("escf", "egrad"):
|
|
701
|
+
with open(self.td_vec_fn) as handle:
|
|
702
|
+
text = handle.read()
|
|
703
|
+
ci_coeffs = self.parse_td_vectors(text)
|
|
704
|
+
ci_coeffs = [cc["vector"] for cc in ci_coeffs]
|
|
705
|
+
# Parse eigenvectors from ricc2 calculation
|
|
706
|
+
elif self.second_cmd == "ricc2":
|
|
707
|
+
ci_coeffs = [self.parse_cc2_vectors(ccre) for ccre in self.ccres]
|
|
708
|
+
|
|
709
|
+
ci_coeffs = np.array(ci_coeffs)
|
|
710
|
+
states = ci_coeffs.shape[0]
|
|
711
|
+
tden_size = self.occ_mos * self.virt_mos
|
|
712
|
+
if ci_coeffs.shape[1] == (2 * tden_size):
|
|
713
|
+
self.log("TDDFT calculation with X and Y vectors present. ")
|
|
714
|
+
XpY = ci_coeffs[:, :tden_size]
|
|
715
|
+
XmY = ci_coeffs[:, tden_size:]
|
|
716
|
+
X = (XpY + XmY) / 2.0
|
|
717
|
+
Y = XpY - X
|
|
718
|
+
else:
|
|
719
|
+
X = ci_coeffs
|
|
720
|
+
Y = np.zeros_like(X)
|
|
721
|
+
tden_shape = (states, self.occ_mos, self.virt_mos)
|
|
722
|
+
X = X.reshape(tden_shape)
|
|
723
|
+
Y = Y.reshape(tden_shape)
|
|
724
|
+
|
|
725
|
+
return X, Y
|
|
726
|
+
|
|
727
|
+
def parse_force(self, path):
|
|
728
|
+
results = parse_turbo_gradient(path)
|
|
729
|
+
return results
|
|
730
|
+
|
|
731
|
+
def parse_hessian(self, path, fn=None):
|
|
732
|
+
if fn is None:
|
|
733
|
+
fn = path / "nprhessian"
|
|
734
|
+
|
|
735
|
+
with open(fn) as handle:
|
|
736
|
+
text = handle.read()
|
|
737
|
+
|
|
738
|
+
split = text.strip().split()
|
|
739
|
+
assert split[0] == "$nprhessian"
|
|
740
|
+
assert split[-1] == "$end"
|
|
741
|
+
|
|
742
|
+
def is_float(str_):
|
|
743
|
+
return "." in str_
|
|
744
|
+
|
|
745
|
+
hess_items = [item for item in split if is_float(item)]
|
|
746
|
+
coord_num = int(sqrt(len(hess_items)))
|
|
747
|
+
assert coord_num**2 == len(hess_items)
|
|
748
|
+
hessian = np.array(hess_items, dtype=float).reshape(-1, coord_num)
|
|
749
|
+
|
|
750
|
+
energy = self.parse_energy(path)["energy"]
|
|
751
|
+
|
|
752
|
+
results = {
|
|
753
|
+
"energy": energy,
|
|
754
|
+
"hessian": hessian,
|
|
755
|
+
}
|
|
756
|
+
return results
|
|
757
|
+
|
|
758
|
+
def parse_td_vectors(self, text):
|
|
759
|
+
"""For TDA calculations only the X vector is present in the
|
|
760
|
+
ciss_a/etc. file. In TDDFT calculations there are twise as
|
|
761
|
+
much items compared with TDA. The first half corresponds to
|
|
762
|
+
(X+Y) and the second half to (X-Y). X can be calculated as
|
|
763
|
+
X = ((X+Y)+(X-Y))/2. Y is then given as Y = (X+Y)-X. The
|
|
764
|
+
normalization can then by checked as
|
|
765
|
+
np.concatenate((X, Y)).dot(np.concatenate((X, -Y)))
|
|
766
|
+
and should be 1."""
|
|
767
|
+
|
|
768
|
+
def to_float(s, loc, toks):
|
|
769
|
+
match = toks[0].replace("D", "E")
|
|
770
|
+
return float(match)
|
|
771
|
+
|
|
772
|
+
float_ = pp.Word(pp.nums + ".-D+").setParseAction(to_float)
|
|
773
|
+
integer = pp.Word(pp.nums).setParseAction(lambda t: int(t[0]))
|
|
774
|
+
float_chrs = pp.nums + "D.+-"
|
|
775
|
+
float_20 = pp.Word(float_chrs, exact=20).setParseAction(to_float)
|
|
776
|
+
|
|
777
|
+
title = pp.Literal("$title")
|
|
778
|
+
symmetry = pp.Literal("$symmetry") + pp.Word(pp.alphanums).setResultsName(
|
|
779
|
+
"symmetry"
|
|
780
|
+
)
|
|
781
|
+
tensor_dim = pp.Literal("$tensor space dimension") + integer.setResultsName(
|
|
782
|
+
"tensor_dim"
|
|
783
|
+
)
|
|
784
|
+
scfinstab = pp.Literal("$scfinstab") + pp.Word(pp.alphanums).setResultsName(
|
|
785
|
+
"scfinstab"
|
|
786
|
+
)
|
|
787
|
+
subspace_dim = pp.Literal(
|
|
788
|
+
"$current subspace dimension"
|
|
789
|
+
) + integer.setResultsName("subspace_dim")
|
|
790
|
+
converged = pp.Literal("$current iteration converged")
|
|
791
|
+
eigenpairs = pp.Literal("$eigenpairs")
|
|
792
|
+
eigenpair = pp.Group(
|
|
793
|
+
integer.setResultsName("state")
|
|
794
|
+
+ pp.Literal("eigenvalue =")
|
|
795
|
+
+ float_.setResultsName("eigenvalue")
|
|
796
|
+
+ pp.Group(pp.OneOrMore(float_20)).setResultsName("vector")
|
|
797
|
+
)
|
|
798
|
+
end = pp.Literal("$end")
|
|
799
|
+
|
|
800
|
+
parser = (
|
|
801
|
+
title
|
|
802
|
+
+ symmetry
|
|
803
|
+
+ tensor_dim
|
|
804
|
+
+ scfinstab
|
|
805
|
+
+ subspace_dim
|
|
806
|
+
+ converged
|
|
807
|
+
+ eigenpairs
|
|
808
|
+
+ pp.OneOrMore(eigenpair).setResultsName("eigenpairs")
|
|
809
|
+
+ end
|
|
810
|
+
)
|
|
811
|
+
result = parser.parseString(text)
|
|
812
|
+
states = result["subspace_dim"]
|
|
813
|
+
eigenpairs = result["eigenpairs"]
|
|
814
|
+
eigenpair_list = [eigenpairs[i].asDict() for i in range(states)]
|
|
815
|
+
return eigenpair_list
|
|
816
|
+
|
|
817
|
+
def parse_cc2_vectors(self, ccre):
|
|
818
|
+
with open(ccre) as handle:
|
|
819
|
+
text = handle.read()
|
|
820
|
+
coeffs = parse_turbo_ccre0_ascii(text)
|
|
821
|
+
coeffs = coeffs.reshape(-1, self.virt_mos)
|
|
822
|
+
|
|
823
|
+
eigenpairs_full = np.zeros((self.occ_mos, self.virt_mos))
|
|
824
|
+
eigenpairs_full[self.frozen_mos :, :] = coeffs
|
|
825
|
+
"""
|
|
826
|
+
from_inds, to_inds = np.where(np.abs(eigenpairs_full) > 0.1)
|
|
827
|
+
for i, (from_, to_) in enumerate(zip(from_inds, to_inds)):
|
|
828
|
+
sq = eigenpairs_full[from_, to_]**2
|
|
829
|
+
print(f"{from_+1:02d} -> {to_+self.occ_mos+1:02d}: {sq:.2%}")
|
|
830
|
+
print()
|
|
831
|
+
"""
|
|
832
|
+
|
|
833
|
+
return eigenpairs_full
|
|
834
|
+
|
|
835
|
+
def parse_gs_energy(self):
|
|
836
|
+
"""Several places are possible:
|
|
837
|
+
$subenergy from control file
|
|
838
|
+
total energy from turbomole.out
|
|
839
|
+
Final MP2 energy from turbomole.out with ADC(2)
|
|
840
|
+
Final CC2 energy from turbomole.out with CC(2)
|
|
841
|
+
"""
|
|
842
|
+
float_re = r"([\d\-\.E]+)"
|
|
843
|
+
regexs = [
|
|
844
|
+
# CC2 ground state energy
|
|
845
|
+
("out", r"Final CC2 energy\s*:\s*" + float_re, 0),
|
|
846
|
+
# ADC(2) ground state energy
|
|
847
|
+
("out", r"Final MP2 energy\s*:\s*" + float_re, 0),
|
|
848
|
+
("control", r"\$subenergy.*$\s*" + float_re, re.MULTILINE),
|
|
849
|
+
# DSCF ground state energy
|
|
850
|
+
("out", r"total energy\s*=\s*" + float_re, 0),
|
|
851
|
+
# From egrad when a rootflip occured. Then only the excited
|
|
852
|
+
# state calculation will be redone and the ground state calculation
|
|
853
|
+
# won't be present in the out-file.
|
|
854
|
+
("out", r"Ground state\s*?Total energy:\s+" + float_re, re.MULTILINE),
|
|
855
|
+
]
|
|
856
|
+
for file_attr, regex, flag in regexs:
|
|
857
|
+
regex_ = re.compile(regex, flags=flag)
|
|
858
|
+
with open(getattr(self, file_attr)) as handle:
|
|
859
|
+
text = handle.read()
|
|
860
|
+
mobj = regex_.search(text)
|
|
861
|
+
try:
|
|
862
|
+
gs_energy = float(mobj[1])
|
|
863
|
+
self.log(
|
|
864
|
+
f"Parsed ground state energy from '{file_attr}' using "
|
|
865
|
+
f"regex '{regex[:11]}'."
|
|
866
|
+
)
|
|
867
|
+
return gs_energy
|
|
868
|
+
except TypeError:
|
|
869
|
+
continue
|
|
870
|
+
raise Exception("Couldn't parse ground state energy!")
|
|
871
|
+
|
|
872
|
+
def prepare_overlap_data(self, path):
|
|
873
|
+
all_energies = self.parse_all_energies()
|
|
874
|
+
X, Y = self.parse_ci_coeffs()
|
|
875
|
+
self.log(f"Reading MO coefficients from '{self.mos}'.")
|
|
876
|
+
with open(self.mos) as handle:
|
|
877
|
+
text = handle.read()
|
|
878
|
+
C = parse_turbo_mos(text)
|
|
879
|
+
self.log(f"Reading electronic energies from '{self.out}'.")
|
|
880
|
+
return C, X, Y, all_energies
|
|
881
|
+
|
|
882
|
+
def run_after(self, path):
|
|
883
|
+
# Convert binary CCRE0 files to ASCII for easier parsing
|
|
884
|
+
for ccre in path.glob("CCRE0-*"):
|
|
885
|
+
cmd = f"ricctools -dump {ccre.name}".split()
|
|
886
|
+
result = subprocess.Popen(
|
|
887
|
+
cmd, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
888
|
+
)
|
|
889
|
+
result.wait()
|
|
890
|
+
# ricctools seem to crash sometimes, even though the respective ASCII
|
|
891
|
+
# ccre-files are generated.
|
|
892
|
+
subprocess.run(
|
|
893
|
+
"actual -r".split(),
|
|
894
|
+
cwd=path,
|
|
895
|
+
stdout=subprocess.PIPE,
|
|
896
|
+
stderr=subprocess.PIPE,
|
|
897
|
+
)
|
|
898
|
+
|
|
899
|
+
if self.td:
|
|
900
|
+
self.make_molden(path)
|
|
901
|
+
# With ricc2 we probably have a frozen core that we have to disable
|
|
902
|
+
# temporarily before creating the molden file. Afterwards we restore
|
|
903
|
+
# the original control file with the frozen core.
|
|
904
|
+
elif self.ricc2:
|
|
905
|
+
# Backup original control file
|
|
906
|
+
ctrl_backup = path / "control.backup"
|
|
907
|
+
shutil.copy(path / "control", ctrl_backup)
|
|
908
|
+
# We have to remove line with implicit core in the control file
|
|
909
|
+
with open(path / "control") as handle:
|
|
910
|
+
text = handle.read()
|
|
911
|
+
lines = text.split("\n")
|
|
912
|
+
lines = [l for l in lines if "implicit core" not in l]
|
|
913
|
+
with open(path / "control", "w") as handle:
|
|
914
|
+
handle.write("\n".join(lines))
|
|
915
|
+
self.make_molden(path)
|
|
916
|
+
# Restore control backup
|
|
917
|
+
shutil.copy(ctrl_backup, path / "control")
|
|
918
|
+
|
|
919
|
+
@staticmethod
|
|
920
|
+
def make_molden(path):
|
|
921
|
+
cmd = "tm2molden norm".split()
|
|
922
|
+
fn = "wavefunction.molden"
|
|
923
|
+
stdin = f"""{fn}
|
|
924
|
+
|
|
925
|
+
"""
|
|
926
|
+
res = subprocess.Popen(
|
|
927
|
+
cmd,
|
|
928
|
+
cwd=path,
|
|
929
|
+
universal_newlines=True,
|
|
930
|
+
stdin=subprocess.PIPE,
|
|
931
|
+
stdout=subprocess.PIPE,
|
|
932
|
+
stderr=subprocess.PIPE,
|
|
933
|
+
)
|
|
934
|
+
stdout, stderr = res.communicate(stdin)
|
|
935
|
+
res.terminate()
|
|
936
|
+
|
|
937
|
+
def get_chkfiles(self):
|
|
938
|
+
if self.uhf:
|
|
939
|
+
chkfiles = {
|
|
940
|
+
"alpha": self.alpha,
|
|
941
|
+
"beta": self.beta,
|
|
942
|
+
}
|
|
943
|
+
else:
|
|
944
|
+
chkfiles = {
|
|
945
|
+
"mos": self.mos,
|
|
946
|
+
}
|
|
947
|
+
return chkfiles
|
|
948
|
+
|
|
949
|
+
def set_chkfiles(self, chkfiles):
|
|
950
|
+
try:
|
|
951
|
+
if self.uhf:
|
|
952
|
+
alpha = chkfiles["alpha"]
|
|
953
|
+
beta = chkfiles["beta"]
|
|
954
|
+
self.alpha = alpha
|
|
955
|
+
self.beta = beta
|
|
956
|
+
self.log(f"Set chkfile '{alpha}' and '{beta}' as alpha and beta.")
|
|
957
|
+
else:
|
|
958
|
+
mos = chkfiles["mos"]
|
|
959
|
+
self.mos = mos
|
|
960
|
+
self.log(f"Set chkfile '{mos}' as mos.")
|
|
961
|
+
except KeyError:
|
|
962
|
+
self.log("Found no chkfile information in chkfiles!")
|
|
963
|
+
|
|
964
|
+
def __str__(self):
|
|
965
|
+
return "Turbomole calculator"
|