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,659 @@
|
|
|
1
|
+
# [1] https://doi.org/10.1063/1.1515483 optimization review
|
|
2
|
+
# [2] https://doi.org/10.1063/1.471864 delocalized internal coordinates
|
|
3
|
+
# [3] https://doi.org/10.1016/0009-2614(95)00646-L lindh model hessian
|
|
4
|
+
# [4] 10.1002/(SICI)1096-987X(19990730)20:10<1067::AID-JCC9>3.0.CO;2-V
|
|
5
|
+
# Handling of corner cases
|
|
6
|
+
# [5] https://doi.org/10.1063/1.462844 , Pulay 1992
|
|
7
|
+
|
|
8
|
+
import itertools as it
|
|
9
|
+
import math
|
|
10
|
+
from operator import itemgetter
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import torch
|
|
14
|
+
|
|
15
|
+
from pysisyphus.config import (
|
|
16
|
+
BEND_MIN_DEG,
|
|
17
|
+
LB_MIN_DEG,
|
|
18
|
+
DIHED_MAX_DEG,
|
|
19
|
+
)
|
|
20
|
+
from pysisyphus.elem_data import get_tm_indices
|
|
21
|
+
from pysisyphus.linalg import svd_inv
|
|
22
|
+
from pysisyphus.intcoords.exceptions import PrimitiveNotDefinedException
|
|
23
|
+
from pysisyphus.intcoords.update import transform_int_step
|
|
24
|
+
from pysisyphus.intcoords.eval import (
|
|
25
|
+
eval_primitives,
|
|
26
|
+
check_primitives,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from pysisyphus.intcoords.logging_conf import logger
|
|
30
|
+
from pysisyphus.intcoords.PrimTypes import (
|
|
31
|
+
normalize_prim_inputs,
|
|
32
|
+
PrimTypes,
|
|
33
|
+
# PrimType classes
|
|
34
|
+
Bonds,
|
|
35
|
+
Bends,
|
|
36
|
+
DummyCoords,
|
|
37
|
+
LinearBends,
|
|
38
|
+
Cartesians,
|
|
39
|
+
Dihedrals,
|
|
40
|
+
OutOfPlanes,
|
|
41
|
+
Rotations,
|
|
42
|
+
Translations,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
from pysisyphus.intcoords.setup import (
|
|
46
|
+
setup_redundant,
|
|
47
|
+
get_primitives,
|
|
48
|
+
)
|
|
49
|
+
from pysisyphus.intcoords.valid import check_typed_prims
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class RedundantCoords:
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
atoms,
|
|
56
|
+
coords3d,
|
|
57
|
+
masses=None,
|
|
58
|
+
bond_factor=1.3,
|
|
59
|
+
typed_prims=None,
|
|
60
|
+
define_prims=None,
|
|
61
|
+
constrain_prims=None,
|
|
62
|
+
freeze_atoms=None,
|
|
63
|
+
freeze_atoms_exclude=False,
|
|
64
|
+
internals_with_frozen=False,
|
|
65
|
+
define_for=None,
|
|
66
|
+
bonds_only=False,
|
|
67
|
+
check_bends=True,
|
|
68
|
+
rebuild=True,
|
|
69
|
+
bend_min_deg=BEND_MIN_DEG,
|
|
70
|
+
dihed_max_deg=DIHED_MAX_DEG,
|
|
71
|
+
lb_min_deg=LB_MIN_DEG,
|
|
72
|
+
weighted=False,
|
|
73
|
+
min_weight=0.3,
|
|
74
|
+
# Corresponds to a threshold of 1e-7 for eigenvalues of G, as proposed by
|
|
75
|
+
# Pulay in [5].
|
|
76
|
+
svd_inv_thresh=3.16e-4,
|
|
77
|
+
recalc_B=False,
|
|
78
|
+
tric=False,
|
|
79
|
+
hybrid=False,
|
|
80
|
+
hbond_angles=False,
|
|
81
|
+
rm_for_frag=None,
|
|
82
|
+
):
|
|
83
|
+
self.atoms = atoms
|
|
84
|
+
self.coords3d = np.reshape(coords3d, (-1, 3)).copy()
|
|
85
|
+
self.masses = masses
|
|
86
|
+
self.bond_factor = bond_factor
|
|
87
|
+
if typed_prims is not None:
|
|
88
|
+
typed_prims = normalize_prim_inputs(typed_prims)
|
|
89
|
+
# Define additional primitives
|
|
90
|
+
if define_prims is None:
|
|
91
|
+
define_prims = list()
|
|
92
|
+
self.define_prims = normalize_prim_inputs(define_prims)
|
|
93
|
+
if freeze_atoms is None:
|
|
94
|
+
freeze_atoms = list()
|
|
95
|
+
self.freeze_atoms = np.array(freeze_atoms, dtype=int)
|
|
96
|
+
self.freeze_atoms_exclude = freeze_atoms_exclude
|
|
97
|
+
self.internals_with_frozen = internals_with_frozen
|
|
98
|
+
self.define_for = define_for
|
|
99
|
+
# Constrain primitives
|
|
100
|
+
if constrain_prims is None:
|
|
101
|
+
constrain_prims = list()
|
|
102
|
+
self.constrain_prims = normalize_prim_inputs(constrain_prims)
|
|
103
|
+
self.bonds_only = bonds_only
|
|
104
|
+
self.check_bends = check_bends
|
|
105
|
+
self.rebuild = rebuild
|
|
106
|
+
self.bend_min_deg = bend_min_deg
|
|
107
|
+
self.dihed_max_deg = dihed_max_deg
|
|
108
|
+
self.lb_min_deg = lb_min_deg
|
|
109
|
+
self.weighted = weighted
|
|
110
|
+
self.min_weight = float(min_weight)
|
|
111
|
+
assert self.min_weight > 0.0, "min_weight must be a positive rational!"
|
|
112
|
+
self.svd_inv_thresh = svd_inv_thresh
|
|
113
|
+
self.recalc_B = recalc_B
|
|
114
|
+
self.tric = tric
|
|
115
|
+
self.hybrid = hybrid
|
|
116
|
+
self.hbond_angles = hbond_angles
|
|
117
|
+
self.rm_for_frag = rm_for_frag
|
|
118
|
+
|
|
119
|
+
self._B_prim = None
|
|
120
|
+
# Lists for the other types of primitives will be created afterwards.
|
|
121
|
+
self.logger = logger
|
|
122
|
+
|
|
123
|
+
if self.weighted:
|
|
124
|
+
self.log(
|
|
125
|
+
"Coordinate weighting requested, min_weight="
|
|
126
|
+
f"{self.min_weight:.2f}. Calculating bond factor."
|
|
127
|
+
)
|
|
128
|
+
# Screening function is
|
|
129
|
+
# ρ(d) = exp(-(d/sum_cov_rad - 1)
|
|
130
|
+
#
|
|
131
|
+
# Swart proposed a min_weight of ρ(d) = 0.3. With this we can
|
|
132
|
+
# calculate the appropriate factor for the bond detection.
|
|
133
|
+
# d = (1 - ln(0.3)) * sum_cov_rad
|
|
134
|
+
# bond_factor = (1 - ln(0.3)) ≈ 2.204
|
|
135
|
+
#
|
|
136
|
+
# The snippet below prints weights and corresponding bond_factors.
|
|
137
|
+
# [f"{w:.2f}: {1-np.log(w):.4f}" for w in np.linspace(0.3, 1, 25)]
|
|
138
|
+
self.bond_factor = -math.log(self.min_weight) + 1
|
|
139
|
+
self.log(f"Using a factor of {self.bond_factor:.6f} for bond detection.")
|
|
140
|
+
self.log(f"Using svd_inv_thresh={self.svd_inv_thresh:.4e} for inversions.")
|
|
141
|
+
|
|
142
|
+
# Set up primitive coordinate indices
|
|
143
|
+
if typed_prims is None:
|
|
144
|
+
self.set_primitive_indices(
|
|
145
|
+
self.atoms,
|
|
146
|
+
self.coords3d,
|
|
147
|
+
)
|
|
148
|
+
# Use supplied typed_prims
|
|
149
|
+
else:
|
|
150
|
+
unique_typed_prims = set(typed_prims) | set(self.define_prims)
|
|
151
|
+
self.typed_prims = list(unique_typed_prims)
|
|
152
|
+
|
|
153
|
+
if self.bonds_only:
|
|
154
|
+
self.typed_prims = self.bond_typed_prims
|
|
155
|
+
|
|
156
|
+
self.primitives = get_primitives(
|
|
157
|
+
self.coords3d,
|
|
158
|
+
self.typed_prims,
|
|
159
|
+
logger=self.logger,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# First evaluation of internal coordinates
|
|
163
|
+
self._prim_internals = self.eval(self.coords3d)
|
|
164
|
+
self._prim_coords = np.array(
|
|
165
|
+
[prim_int.val for prim_int in self._prim_internals]
|
|
166
|
+
)
|
|
167
|
+
check_primitives(
|
|
168
|
+
self.coords3d, self.primitives, B=self.B_prim, logger=self.logger
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
ref_num = len(self.typed_prims)
|
|
172
|
+
if self.bonds_only:
|
|
173
|
+
ref_num = len(self.bond_indices)
|
|
174
|
+
assert len(self.primitives) == ref_num
|
|
175
|
+
|
|
176
|
+
self.backtransform_counter = 0
|
|
177
|
+
|
|
178
|
+
def set_inds_from_typed_prims(self, typed_prims):
|
|
179
|
+
# These lists will hold the index of the respective typed_prims
|
|
180
|
+
# in 'self.typed_prims'.
|
|
181
|
+
self._bond_inds = list()
|
|
182
|
+
self._bend_inds = list()
|
|
183
|
+
self._linear_bend_inds = list()
|
|
184
|
+
self._dihedral_inds = list()
|
|
185
|
+
self._rotation_inds = list()
|
|
186
|
+
self._translation_inds = list()
|
|
187
|
+
self._cartesian_inds = list()
|
|
188
|
+
self._outofplane_inds = list()
|
|
189
|
+
self._dummycoord_inds = list()
|
|
190
|
+
self._cartesian_inds = list()
|
|
191
|
+
|
|
192
|
+
self._bond_atom_inds = list()
|
|
193
|
+
self._bend_atom_inds = list()
|
|
194
|
+
self._dihedral_atom_inds = list()
|
|
195
|
+
|
|
196
|
+
self._bond_typed_prims = list()
|
|
197
|
+
|
|
198
|
+
for i, (pt, *indices) in enumerate(typed_prims):
|
|
199
|
+
if pt in Bonds:
|
|
200
|
+
append_to = self._bond_inds
|
|
201
|
+
self._bond_atom_inds.append(indices)
|
|
202
|
+
self._bond_typed_prims.append((pt, *indices))
|
|
203
|
+
elif pt in Bends:
|
|
204
|
+
append_to = self._bend_inds
|
|
205
|
+
self._bend_atom_inds.append(indices)
|
|
206
|
+
elif pt in LinearBends:
|
|
207
|
+
append_to = self._linear_bend_inds
|
|
208
|
+
elif pt in Dihedrals:
|
|
209
|
+
append_to = self._dihedral_inds
|
|
210
|
+
self._dihedral_atom_inds.append(indices)
|
|
211
|
+
elif pt in Rotations:
|
|
212
|
+
append_to = self._rotation_inds
|
|
213
|
+
elif pt in Translations:
|
|
214
|
+
append_to = self._translation_inds
|
|
215
|
+
elif pt in Cartesians:
|
|
216
|
+
append_to = self._cartesian_inds
|
|
217
|
+
elif pt in OutOfPlanes:
|
|
218
|
+
append_to = self._outofplane_inds
|
|
219
|
+
elif pt in DummyCoords:
|
|
220
|
+
append_to = self._dummycoord_inds
|
|
221
|
+
elif pt in Cartesians:
|
|
222
|
+
append_to = self._cartesian_inds
|
|
223
|
+
else:
|
|
224
|
+
raise Exception("Unhandled PrimType!")
|
|
225
|
+
append_to.append(i)
|
|
226
|
+
|
|
227
|
+
def log(self, message):
|
|
228
|
+
self.logger.debug(message)
|
|
229
|
+
|
|
230
|
+
def clear(self):
|
|
231
|
+
self._B_prim = None
|
|
232
|
+
self._prim_coords = None
|
|
233
|
+
self._prim_internals = None
|
|
234
|
+
self._P = None
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def coords3d(self):
|
|
238
|
+
return self._coords3d
|
|
239
|
+
|
|
240
|
+
@coords3d.setter
|
|
241
|
+
def coords3d(self, coords3d):
|
|
242
|
+
self._coords3d = coords3d.reshape(-1, 3)
|
|
243
|
+
self.clear()
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def typed_prims(self):
|
|
247
|
+
return self._typed_prims
|
|
248
|
+
|
|
249
|
+
@typed_prims.setter
|
|
250
|
+
def typed_prims(self, typed_prims):
|
|
251
|
+
self.log(f"Checking {len(typed_prims)} supplied typed primitives.")
|
|
252
|
+
valid_typed_prims = check_typed_prims(
|
|
253
|
+
self.coords3d,
|
|
254
|
+
typed_prims,
|
|
255
|
+
bend_min_deg=self.bend_min_deg,
|
|
256
|
+
dihed_max_deg=self.dihed_max_deg,
|
|
257
|
+
lb_min_deg=self.lb_min_deg,
|
|
258
|
+
check_bends=self.check_bends,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def tp_sort(tp):
|
|
262
|
+
pt, *indices = tp
|
|
263
|
+
key = pt
|
|
264
|
+
# We use the fact that list.sort is stable, that is elements that compare
|
|
265
|
+
# equal retain their order. So we assign PrimTypes.ROTATION to all rotations,
|
|
266
|
+
# to remain the ABC-order for each fragment. The same goes for the translations.
|
|
267
|
+
if pt in Rotations:
|
|
268
|
+
key = PrimTypes.ROTATION
|
|
269
|
+
elif pt in Translations:
|
|
270
|
+
key = PrimTypes.TRANSLATION
|
|
271
|
+
elif pt in Cartesians:
|
|
272
|
+
key = PrimTypes.CARTESIAN
|
|
273
|
+
return (key, *indices)
|
|
274
|
+
|
|
275
|
+
# Sort by PrimType
|
|
276
|
+
valid_typed_prims.sort(key=tp_sort)
|
|
277
|
+
|
|
278
|
+
self.log(
|
|
279
|
+
f"{len(valid_typed_prims)} primitives are valid at the current Cartesians."
|
|
280
|
+
)
|
|
281
|
+
if len(valid_typed_prims) != len(typed_prims):
|
|
282
|
+
self.log("Invalid primitives:")
|
|
283
|
+
for i, invalid_prim in enumerate(set(typed_prims) - set(valid_typed_prims)):
|
|
284
|
+
self.log(f"\t{i:02d}: {invalid_prim}")
|
|
285
|
+
self._typed_prims = valid_typed_prims
|
|
286
|
+
self.set_inds_from_typed_prims(self.typed_prims)
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def primitives(self):
|
|
290
|
+
return self._primitives
|
|
291
|
+
|
|
292
|
+
@primitives.setter
|
|
293
|
+
def primitives(self, primitives):
|
|
294
|
+
self._primitives = primitives
|
|
295
|
+
|
|
296
|
+
@property
|
|
297
|
+
def prim_indices_set(self):
|
|
298
|
+
return set([tuple(indices) for pt, *indices in self.typed_prims])
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def prim_internals(self):
|
|
302
|
+
if self._prim_internals is None:
|
|
303
|
+
self._prim_internals = self.eval(self.coords3d)
|
|
304
|
+
return self._prim_internals
|
|
305
|
+
|
|
306
|
+
@prim_internals.setter
|
|
307
|
+
def prim_internals(self, prim_internals):
|
|
308
|
+
self._prim_internals = prim_internals
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def prim_coords(self):
|
|
312
|
+
return np.array([prim_int.val for prim_int in self.prim_internals])
|
|
313
|
+
|
|
314
|
+
def return_inds(self, slice_):
|
|
315
|
+
return np.array([prim_int.indices for prim_int in self.prim_internals[slice_]])
|
|
316
|
+
|
|
317
|
+
def get_prim_internals_by_indices(self, indices):
|
|
318
|
+
if len(indices) == 0:
|
|
319
|
+
pis = []
|
|
320
|
+
elif len(indices) == 1:
|
|
321
|
+
pis = [self.prim_internals[indices[0]]]
|
|
322
|
+
else:
|
|
323
|
+
pis = itemgetter(*indices)(self.prim_internals)
|
|
324
|
+
return pis
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def bond_indices(self):
|
|
328
|
+
return self._bond_inds
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def bond_atom_indices(self):
|
|
332
|
+
return self._bond_atom_inds
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def bond_typed_prims(self):
|
|
336
|
+
return self._bond_typed_prims
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def bend_indices(self):
|
|
340
|
+
return self._bend_inds
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def bend_atom_indices(self):
|
|
344
|
+
return self._bend_atom_inds
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
def linear_bend_indices(self):
|
|
348
|
+
return self._linear_bend_inds
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def dihedral_indices(self):
|
|
352
|
+
return self._dihedral_inds
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def dihedral_atom_indices(self):
|
|
356
|
+
return self._dihedral_atom_inds
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
def rotation_indices(self):
|
|
360
|
+
return self._rotation_inds
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def translation_indices(self):
|
|
364
|
+
return self._translation_inds
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
def cartesian_indices(self):
|
|
368
|
+
return self._cartesian_inds
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def outofplane_indices(self):
|
|
372
|
+
return self._outofplane_inds
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def coords(self):
|
|
376
|
+
return self.prim_coords
|
|
377
|
+
|
|
378
|
+
def get_index_of_typed_prim(self, typed_prim):
|
|
379
|
+
"""Index in self.typed_prims for the supplied typed_prim."""
|
|
380
|
+
ref_len = len(typed_prim)
|
|
381
|
+
ref_inds = typed_prim[1:]
|
|
382
|
+
for i, tp in enumerate(self.typed_prims):
|
|
383
|
+
if (len(tp) != ref_len) or tp[0] != typed_prim[0]:
|
|
384
|
+
continue
|
|
385
|
+
|
|
386
|
+
if (tp[1:] == ref_inds) or (tp[1:] == ref_inds[::-1]):
|
|
387
|
+
return i
|
|
388
|
+
self.log(f"Typed primitive {typed_prim} is not defined!")
|
|
389
|
+
raise PrimitiveNotDefinedException(typed_prim)
|
|
390
|
+
|
|
391
|
+
@property
|
|
392
|
+
def B_prim(self):
|
|
393
|
+
"""Wilson B-Matrix"""
|
|
394
|
+
if self._B_prim is None:
|
|
395
|
+
self._B_prim = np.array([prim_int.grad for prim_int in self.prim_internals])
|
|
396
|
+
|
|
397
|
+
return self._B_prim
|
|
398
|
+
|
|
399
|
+
@property
|
|
400
|
+
def B(self):
|
|
401
|
+
"""Wilson B-Matrix"""
|
|
402
|
+
return self.B_prim
|
|
403
|
+
|
|
404
|
+
def inv_B(self, B):
|
|
405
|
+
return B.T.dot(svd_inv(B.dot(B.T), thresh=self.svd_inv_thresh, hermitian=True))
|
|
406
|
+
|
|
407
|
+
def inv_Bt(self, B):
|
|
408
|
+
return svd_inv(B.dot(B.T), thresh=self.svd_inv_thresh, hermitian=True).dot(B)
|
|
409
|
+
|
|
410
|
+
@property
|
|
411
|
+
def Bt_inv_prim(self):
|
|
412
|
+
"""Transposed generalized inverse of the primitive Wilson B-Matrix."""
|
|
413
|
+
return self.inv_Bt(self.B_prim)
|
|
414
|
+
|
|
415
|
+
@property
|
|
416
|
+
def Bt_inv(self):
|
|
417
|
+
"""Transposed generalized inverse of the Wilson B-Matrix."""
|
|
418
|
+
return self.inv_Bt(self.B)
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def B_inv_prim(self):
|
|
422
|
+
"""Generalized inverse of the primitive Wilson B-Matrix."""
|
|
423
|
+
return self.inv_B(self.B_prim)
|
|
424
|
+
|
|
425
|
+
@property
|
|
426
|
+
def B_inv(self):
|
|
427
|
+
"""Generalized inverse of the Wilson B-Matrix."""
|
|
428
|
+
return self.inv_B(self.B)
|
|
429
|
+
|
|
430
|
+
@property
|
|
431
|
+
def constrained_indices(self):
|
|
432
|
+
return [self.typed_prims.index(cp) for cp in self.constrain_prims]
|
|
433
|
+
|
|
434
|
+
@property
|
|
435
|
+
def C(self):
|
|
436
|
+
"""Diagonal matrix. Entries for constraints are set to one."""
|
|
437
|
+
size = len(self.typed_prims)
|
|
438
|
+
C = np.zeros((size, size))
|
|
439
|
+
inds = self.constrained_indices
|
|
440
|
+
C[inds, inds] = 1
|
|
441
|
+
return C
|
|
442
|
+
|
|
443
|
+
@property
|
|
444
|
+
def P(self):
|
|
445
|
+
"""Projection matrix onto B. See [1] Eq. (4)."""
|
|
446
|
+
if self._P is None:
|
|
447
|
+
P = self.B.dot(self.B_inv)
|
|
448
|
+
# Modify projector, so constrained coordinates are projected out.
|
|
449
|
+
if self.constrain_prims:
|
|
450
|
+
C = self.C
|
|
451
|
+
CPC_inv = svd_inv(C.dot(P).dot(C), thresh=self.svd_inv_thresh)
|
|
452
|
+
P = P - P.dot(C).dot(CPC_inv).dot(C).dot(P)
|
|
453
|
+
self._P = P
|
|
454
|
+
return self._P
|
|
455
|
+
|
|
456
|
+
def transform_forces(self, cart_forces):
|
|
457
|
+
"""Combination of Eq. (9) and (11) in [1]."""
|
|
458
|
+
if isinstance(cart_forces, torch.Tensor):
|
|
459
|
+
P = self._as_torch_like(self.P, cart_forces)
|
|
460
|
+
Bt_inv = self._as_torch_like(self.Bt_inv, cart_forces)
|
|
461
|
+
return P @ (Bt_inv @ cart_forces)
|
|
462
|
+
return self.P.dot(self.Bt_inv.dot(cart_forces))
|
|
463
|
+
|
|
464
|
+
def get_K_matrix(self, int_gradient=None):
|
|
465
|
+
if int_gradient is not None:
|
|
466
|
+
assert len(int_gradient) == len(self._primitives)
|
|
467
|
+
|
|
468
|
+
size_ = self.coords3d.size
|
|
469
|
+
if int_gradient is None:
|
|
470
|
+
return np.zeros((size_, size_))
|
|
471
|
+
|
|
472
|
+
K_flat = np.zeros(size_ * size_)
|
|
473
|
+
coords3d = self.coords3d
|
|
474
|
+
for primitive, int_grad_item in zip(self.primitives, int_gradient):
|
|
475
|
+
if hasattr(int_grad_item, "item"):
|
|
476
|
+
int_grad_item = float(int_grad_item.item())
|
|
477
|
+
# Contract with gradient
|
|
478
|
+
try:
|
|
479
|
+
dg = int_grad_item * primitive.jacobian(coords3d)
|
|
480
|
+
# 2nd derivative of normal, but linear, bends is undefined.
|
|
481
|
+
except (ValueError, ZeroDivisionError):
|
|
482
|
+
self.log(
|
|
483
|
+
"Error in calculation of 2nd derivative of primitive "
|
|
484
|
+
f"internal {primitive.indices}."
|
|
485
|
+
)
|
|
486
|
+
continue
|
|
487
|
+
# Depending on the type of internal coordinate dg is a flat array
|
|
488
|
+
# of size 36 (stretch), 81 (bend) or 144 (torsion).
|
|
489
|
+
#
|
|
490
|
+
# An internal coordinate contributes to an element K[j, k] of the
|
|
491
|
+
# K matrix if the cartesian coordinate indices j and k belong to an
|
|
492
|
+
# atom that contributes to the respective internal coordinate.
|
|
493
|
+
#
|
|
494
|
+
# As for now we build up the K matrix as flat array. To add the dg
|
|
495
|
+
# entries at the appropriate places in K_flat we have to calculate
|
|
496
|
+
# the corresponding flat indices of dg in K_flat.
|
|
497
|
+
cart_inds = list(
|
|
498
|
+
it.chain(*[range(3 * i, 3 * i + 3) for i in primitive.indices])
|
|
499
|
+
)
|
|
500
|
+
flat_inds = [
|
|
501
|
+
row * size_ + col for row, col in it.product(cart_inds, cart_inds)
|
|
502
|
+
]
|
|
503
|
+
K_flat[flat_inds] += dg
|
|
504
|
+
K = K_flat.reshape(size_, size_)
|
|
505
|
+
return K
|
|
506
|
+
|
|
507
|
+
def _as_torch_like(self, array, like):
|
|
508
|
+
if isinstance(array, torch.Tensor):
|
|
509
|
+
return array.to(dtype=like.dtype, device=like.device)
|
|
510
|
+
return torch.tensor(array, dtype=like.dtype, device=like.device)
|
|
511
|
+
|
|
512
|
+
def log_int_grad_msg(self, int_gradient):
|
|
513
|
+
if int_gradient is None:
|
|
514
|
+
self.log(
|
|
515
|
+
"Supplied 'int_gradient' is None. K matrix will be zero, "
|
|
516
|
+
"so derivatives of the\nWilson-B-matrix are neglected in "
|
|
517
|
+
"Hessian transformation."
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
def transform_hessian(self, cart_hessian, int_gradient=None):
|
|
521
|
+
"""Transform Cartesian Hessian to internal coordinates."""
|
|
522
|
+
self.log_int_grad_msg(int_gradient)
|
|
523
|
+
K = self.get_K_matrix(int_gradient)
|
|
524
|
+
if isinstance(cart_hessian, torch.Tensor):
|
|
525
|
+
K = self._as_torch_like(K, cart_hessian)
|
|
526
|
+
Bt_inv_prim = self._as_torch_like(self.Bt_inv_prim, cart_hessian)
|
|
527
|
+
B_inv_prim = self._as_torch_like(self.B_inv_prim, cart_hessian)
|
|
528
|
+
return Bt_inv_prim @ (cart_hessian - K) @ B_inv_prim
|
|
529
|
+
return self.Bt_inv_prim.dot(cart_hessian - K).dot(self.B_inv_prim)
|
|
530
|
+
|
|
531
|
+
def backtransform_hessian(self, redund_hessian, int_gradient=None):
|
|
532
|
+
"""Transform Hessian in internal coordinates to Cartesians."""
|
|
533
|
+
self.log_int_grad_msg(int_gradient)
|
|
534
|
+
K = self.get_K_matrix(int_gradient)
|
|
535
|
+
if isinstance(redund_hessian, torch.Tensor):
|
|
536
|
+
K = self._as_torch_like(K, redund_hessian)
|
|
537
|
+
B = self._as_torch_like(self.B, redund_hessian)
|
|
538
|
+
return B.T @ redund_hessian @ B + K
|
|
539
|
+
return self.B.T.dot(redund_hessian).dot(self.B) + K
|
|
540
|
+
|
|
541
|
+
def project_hessian(self, H, shift=1000):
|
|
542
|
+
"""Expects a hessian in internal coordinates. See Eq. (11) in [1]."""
|
|
543
|
+
P = self.P
|
|
544
|
+
if isinstance(H, torch.Tensor):
|
|
545
|
+
P = self._as_torch_like(P, H)
|
|
546
|
+
eye = torch.eye(P.shape[0], device=H.device, dtype=H.dtype)
|
|
547
|
+
return P @ H @ P + shift * (eye - P)
|
|
548
|
+
return P.dot(H).dot(P) + shift * (np.eye(P.shape[0]) - P)
|
|
549
|
+
|
|
550
|
+
def project_vector(self, vector):
|
|
551
|
+
"""Project supplied vector onto range of B."""
|
|
552
|
+
return self.P.dot(vector)
|
|
553
|
+
|
|
554
|
+
def set_primitive_indices(
|
|
555
|
+
self,
|
|
556
|
+
atoms,
|
|
557
|
+
coords3d,
|
|
558
|
+
):
|
|
559
|
+
coord_info = setup_redundant(
|
|
560
|
+
atoms,
|
|
561
|
+
coords3d,
|
|
562
|
+
factor=self.bond_factor,
|
|
563
|
+
define_prims=self.define_prims,
|
|
564
|
+
min_deg=self.bend_min_deg,
|
|
565
|
+
dihed_max_deg=self.dihed_max_deg,
|
|
566
|
+
lb_min_deg=self.lb_min_deg,
|
|
567
|
+
min_weight=self.min_weight if self.weighted else None,
|
|
568
|
+
tric=self.tric,
|
|
569
|
+
hybrid=self.hybrid,
|
|
570
|
+
hbond_angles=self.hbond_angles,
|
|
571
|
+
freeze_atoms=self.freeze_atoms if self.freeze_atoms_exclude else None,
|
|
572
|
+
internals_with_frozen=self.internals_with_frozen,
|
|
573
|
+
define_for=self.define_for,
|
|
574
|
+
rm_for_frag=self.rm_for_frag,
|
|
575
|
+
logger=self.logger,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
self.typed_prims = coord_info.typed_prims
|
|
579
|
+
for cp in self.constrain_prims:
|
|
580
|
+
if cp not in self.typed_prims:
|
|
581
|
+
self.typed_prims.append(cp)
|
|
582
|
+
|
|
583
|
+
self.fragments = coord_info.fragments
|
|
584
|
+
|
|
585
|
+
def eval(self, coords3d, attr=None):
|
|
586
|
+
prim_internals = eval_primitives(coords3d, self.primitives)
|
|
587
|
+
|
|
588
|
+
if attr is not None:
|
|
589
|
+
return np.array(
|
|
590
|
+
[getattr(prim_internal, attr) for prim_internal in prim_internals]
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
return prim_internals
|
|
594
|
+
|
|
595
|
+
def transform_int_step(self, int_step, update_constraints=False, pure=False):
|
|
596
|
+
self.log(f"Backtransformation {self.backtransform_counter}")
|
|
597
|
+
|
|
598
|
+
def Bt_inv_prim_getter(cart_coords):
|
|
599
|
+
coords3d = cart_coords.reshape(-1, 3)
|
|
600
|
+
B_prim = np.zeros((len(self.primitives), coords3d.size))
|
|
601
|
+
for i, primitive in enumerate(self.primitives):
|
|
602
|
+
_, gradient = primitive.calculate(coords3d, gradient=True)
|
|
603
|
+
B_prim[i] = gradient
|
|
604
|
+
return self.inv_Bt(B_prim)
|
|
605
|
+
|
|
606
|
+
new_prim_internals, cart_step, failed = transform_int_step(
|
|
607
|
+
int_step,
|
|
608
|
+
self.coords3d.flatten(),
|
|
609
|
+
self.prim_coords,
|
|
610
|
+
self.Bt_inv_prim,
|
|
611
|
+
self.primitives,
|
|
612
|
+
typed_prims=self.typed_prims,
|
|
613
|
+
check_dihedrals=self.rebuild,
|
|
614
|
+
check_bends=self.rebuild,
|
|
615
|
+
bend_min_deg=self.bend_min_deg,
|
|
616
|
+
bend_max_deg=self.lb_min_deg,
|
|
617
|
+
freeze_atoms=self.freeze_atoms,
|
|
618
|
+
constrained_inds=self.constrained_indices,
|
|
619
|
+
update_constraints=update_constraints,
|
|
620
|
+
logger=self.logger,
|
|
621
|
+
Bt_inv_prim_getter=Bt_inv_prim_getter if self.recalc_B else None,
|
|
622
|
+
)
|
|
623
|
+
# Update coordinates
|
|
624
|
+
if not pure:
|
|
625
|
+
self.coords3d += cart_step.reshape(-1, 3)
|
|
626
|
+
self.prim_internals = new_prim_internals
|
|
627
|
+
self.backtransform_counter += 1
|
|
628
|
+
return cart_step
|
|
629
|
+
|
|
630
|
+
def print_typed_prims(self):
|
|
631
|
+
for i, tp in enumerate(self.typed_prims):
|
|
632
|
+
print(i, tp)
|
|
633
|
+
|
|
634
|
+
def __str__(self):
|
|
635
|
+
bonds = len(self.bond_indices)
|
|
636
|
+
bends = len(self.bending_indices)
|
|
637
|
+
dihedrals = len(self.dihedral_indices)
|
|
638
|
+
name = self.__class__.__name__
|
|
639
|
+
return f"{name}({bonds} bonds, {bends} bends, {dihedrals} dihedrals)"
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
class TRIC(RedundantCoords):
|
|
643
|
+
def __init__(self, *args, **kwargs):
|
|
644
|
+
kwargs["tric"] = True
|
|
645
|
+
kwargs["recalc_B"] = True
|
|
646
|
+
super().__init__(*args, **kwargs)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
class TMTRIC(TRIC):
|
|
650
|
+
def __init__(self, atoms, *args, **kwargs):
|
|
651
|
+
tm_indices = get_tm_indices(atoms)
|
|
652
|
+
kwargs.setdefault("rm_for_frag", set()).update(tm_indices)
|
|
653
|
+
super().__init__(atoms, *args, **kwargs)
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
class HybridRedundantCoords(RedundantCoords):
|
|
657
|
+
def __init__(self, *args, **kwargs):
|
|
658
|
+
kwargs["hybrid"] = True
|
|
659
|
+
super().__init__(*args, **kwargs)
|