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,669 @@
|
|
|
1
|
+
# [1] https://doi.org/10.1002/jcc.26495
|
|
2
|
+
# Habershon, 2021
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
prp a901cdfacc579eb63b193cbc9043212e8b57746f
|
|
6
|
+
pysis 340ab6105ac4156f0613b4d0e8f080d9f195530c
|
|
7
|
+
do_trans accidentally disabled in transtorque
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from functools import reduce
|
|
11
|
+
import itertools as it
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from pysisyphus.calculators import (
|
|
16
|
+
HardSphere,
|
|
17
|
+
TransTorque,
|
|
18
|
+
AtomAtomTransTorque,
|
|
19
|
+
Composite,
|
|
20
|
+
)
|
|
21
|
+
from pysisyphus.constants import BOHR2ANG
|
|
22
|
+
from pysisyphus.Geometry import Geometry
|
|
23
|
+
from pysisyphus.helpers import align_coords
|
|
24
|
+
from pysisyphus.helpers_pure import highlight_text
|
|
25
|
+
from pysisyphus.init_logging import init_logging
|
|
26
|
+
from pysisyphus.intcoords.setup import get_fragments, get_bond_sets
|
|
27
|
+
from pysisyphus.xyzloader import coords_to_trj, make_xyz_str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
init_logging()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SteepestDescent:
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
geom,
|
|
37
|
+
max_cycles=1000,
|
|
38
|
+
max_step=0.05,
|
|
39
|
+
rms_force=0.05,
|
|
40
|
+
rms_force_only=True,
|
|
41
|
+
prefix=None,
|
|
42
|
+
dump=False,
|
|
43
|
+
dump_every=100,
|
|
44
|
+
print_every=100,
|
|
45
|
+
):
|
|
46
|
+
self.geom = geom
|
|
47
|
+
self.max_cycles = max_cycles
|
|
48
|
+
self.max_step = max_step
|
|
49
|
+
self.rms_force = rms_force
|
|
50
|
+
self.rms_force_only = rms_force_only
|
|
51
|
+
if prefix is not None:
|
|
52
|
+
prefix = f"{prefix}_"
|
|
53
|
+
self.prefix = prefix
|
|
54
|
+
self.dump = dump
|
|
55
|
+
self.dump_every = dump_every
|
|
56
|
+
self.print_every = print_every
|
|
57
|
+
|
|
58
|
+
self.all_coords = np.zeros((max_cycles, self.geom.coords.size))
|
|
59
|
+
|
|
60
|
+
def run(self):
|
|
61
|
+
coords = self.geom.coords.copy()
|
|
62
|
+
|
|
63
|
+
to_dump = []
|
|
64
|
+
|
|
65
|
+
for i in range(self.max_cycles):
|
|
66
|
+
self.all_coords[i] = coords.copy()
|
|
67
|
+
if self.dump and (i % self.dump_every) == 0:
|
|
68
|
+
to_dump.append(self.geom.as_xyz(cart_coords=coords))
|
|
69
|
+
results = self.geom.get_energy_and_forces_at(coords)
|
|
70
|
+
forces = results["forces"]
|
|
71
|
+
norm = np.linalg.norm(forces)
|
|
72
|
+
rms = np.sqrt(np.mean(forces**2))
|
|
73
|
+
|
|
74
|
+
if i > 0:
|
|
75
|
+
beta = forces.dot(forces) / self.prev_forces.dot(self.prev_forces)
|
|
76
|
+
step = forces + beta * self.prev_step
|
|
77
|
+
else:
|
|
78
|
+
step = forces.copy()
|
|
79
|
+
|
|
80
|
+
step *= min(self.max_step / np.abs(step).max(), 1)
|
|
81
|
+
converged = rms <= self.rms_force
|
|
82
|
+
|
|
83
|
+
# Append step information when not yet converged
|
|
84
|
+
if i % self.print_every == 0 or converged:
|
|
85
|
+
print(
|
|
86
|
+
f"{i:05d}: |forces|={norm: >12.6f} "
|
|
87
|
+
f"rms(forces)={np.sqrt(np.mean(forces**2)): >12.6f} "
|
|
88
|
+
f" |step|={np.linalg.norm(step): >12.6f}"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if converged:
|
|
92
|
+
print(f"Converged in cycle {i}. Breaking.")
|
|
93
|
+
break
|
|
94
|
+
|
|
95
|
+
coords += step
|
|
96
|
+
|
|
97
|
+
self.prev_step = step
|
|
98
|
+
self.prev_forces = forces
|
|
99
|
+
self.geom.coords = coords
|
|
100
|
+
self.all_coords = self.all_coords[: i + 1]
|
|
101
|
+
|
|
102
|
+
if to_dump:
|
|
103
|
+
with open(f"{self.prefix}optimization_trj.xyz", "w") as handle:
|
|
104
|
+
handle.write("\n".join(to_dump))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_fragments_and_bonds(geoms):
|
|
108
|
+
if isinstance(geoms, Geometry) or len(geoms) == 1:
|
|
109
|
+
geom = geoms
|
|
110
|
+
atoms = geom.atoms
|
|
111
|
+
coords3d = geom.coords3d
|
|
112
|
+
bonds = [frozenset(bond) for bond in get_bond_sets(atoms, coords3d)]
|
|
113
|
+
fragments = get_fragments(atoms, coords3d.flatten(), bond_inds=bonds)
|
|
114
|
+
frag_inds = list(it.chain(*fragments))
|
|
115
|
+
if len(frag_inds) != len(atoms):
|
|
116
|
+
all_inds = list(range(len(atoms)))
|
|
117
|
+
missing_inds = set(all_inds) - set(frag_inds)
|
|
118
|
+
for mi in missing_inds:
|
|
119
|
+
fragments.append(frozenset((mi,)))
|
|
120
|
+
|
|
121
|
+
frag_bonds = [
|
|
122
|
+
list(filter(lambda bond: bond <= frag, bonds)) for frag in fragments
|
|
123
|
+
]
|
|
124
|
+
# frag_atoms = [[a for i, a in enumerate(atoms) if i in frag] for frag in fragments]
|
|
125
|
+
|
|
126
|
+
# Assert that we do not have any interfragment bonds
|
|
127
|
+
assert reduce((lambda x, y: x + len(y)), frag_bonds, 0) == len(bonds)
|
|
128
|
+
union_geom = geom.copy(coord_type="cart")
|
|
129
|
+
else:
|
|
130
|
+
# Form union, determine consistent new indices for all atoms and calculate bonds
|
|
131
|
+
raise Exception()
|
|
132
|
+
|
|
133
|
+
# return fragments, frag_bonds, set(bonds), frag_atoms
|
|
134
|
+
return fragments, frag_bonds, set(bonds), union_geom
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_rot_mat(coords3d_1, coords3d_2, center=False):
|
|
138
|
+
coords3d_1 = coords3d_1.copy().reshape(-1, 3)
|
|
139
|
+
coords3d_2 = coords3d_2.copy().reshape(-1, 3)
|
|
140
|
+
|
|
141
|
+
def _center(coords3d):
|
|
142
|
+
return coords3d - coords3d.mean(axis=0)
|
|
143
|
+
|
|
144
|
+
if center:
|
|
145
|
+
coords3d_1 = _center(coords3d_1)
|
|
146
|
+
coords3d_2 = _center(coords3d_2)
|
|
147
|
+
|
|
148
|
+
tmp_mat = coords3d_1.T.dot(coords3d_2)
|
|
149
|
+
U, W, Vt = np.linalg.svd(tmp_mat)
|
|
150
|
+
rot_mat = U.dot(Vt)
|
|
151
|
+
# Avoid reflections
|
|
152
|
+
if np.linalg.det(rot_mat) < 0:
|
|
153
|
+
U[:, -1] *= -1
|
|
154
|
+
rot_mat = U.dot(Vt)
|
|
155
|
+
return rot_mat
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def get_steps_to_active_atom_mean(
|
|
159
|
+
frag_lists, iter_frag_lists, ind_dict, coords3d, skip=True
|
|
160
|
+
):
|
|
161
|
+
frag_num = len(frag_lists)
|
|
162
|
+
steps = np.zeros((frag_num, 3))
|
|
163
|
+
for m, frag_m in enumerate(frag_lists):
|
|
164
|
+
step_m = np.zeros(3)
|
|
165
|
+
for n, _ in enumerate(iter_frag_lists):
|
|
166
|
+
if skip and m == n:
|
|
167
|
+
continue
|
|
168
|
+
active_inds = ind_dict[(n, m)]
|
|
169
|
+
if len(active_inds) == 0:
|
|
170
|
+
continue
|
|
171
|
+
step_m += coords3d[active_inds].mean(axis=0)
|
|
172
|
+
step_m /= frag_num
|
|
173
|
+
steps[m] = step_m
|
|
174
|
+
return steps
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def report_frags(rgeom, pgeom, rfrags, pfrags, rbond_diff, pbond_diff):
|
|
178
|
+
for name, geom in (("Reactant(s)", rgeom), ("Product(s)", pgeom)):
|
|
179
|
+
print(f"{name}: {geom}\n\n{geom.as_xyz()}\n")
|
|
180
|
+
|
|
181
|
+
def get_frag_atoms(geom, frag):
|
|
182
|
+
atoms = geom.atoms
|
|
183
|
+
return [atoms[i] for i in frag]
|
|
184
|
+
|
|
185
|
+
for name, geom, frags in (("reactant", rgeom, rfrags), ("product", pgeom, pfrags)):
|
|
186
|
+
print(f"{len(frags)} Fragment(s) in {name} image:\n")
|
|
187
|
+
for frag in frags:
|
|
188
|
+
frag_atoms = get_frag_atoms(geom, frag)
|
|
189
|
+
frag_coords = geom.coords3d[list(frag)]
|
|
190
|
+
frag_xyz = make_xyz_str(frag_atoms, frag_coords * BOHR2ANG)
|
|
191
|
+
print(frag_xyz + "\n")
|
|
192
|
+
|
|
193
|
+
def print_bonds(geom, bonds):
|
|
194
|
+
for from_, to_ in bonds:
|
|
195
|
+
from_atom, to_atom = [geom.atoms[i] for i in (from_, to_)]
|
|
196
|
+
print(f"\t({from_: >3d}{from_atom} - {to_: >3d}{to_atom})")
|
|
197
|
+
|
|
198
|
+
print("Bonds broken in reactant image:")
|
|
199
|
+
print_bonds(rgeom, rbond_diff)
|
|
200
|
+
print()
|
|
201
|
+
print("Bonds formed in product image:")
|
|
202
|
+
print_bonds(pgeom, pbond_diff)
|
|
203
|
+
print()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def report_mats(name, mats):
|
|
207
|
+
for (m, n), indices in mats.items():
|
|
208
|
+
print(f"{name}({m}, {n}): {indices}")
|
|
209
|
+
print()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def center_fragments(frag_list, geom):
|
|
213
|
+
c3d = geom.coords3d
|
|
214
|
+
for frag in frag_list:
|
|
215
|
+
mean = c3d[frag].mean(axis=0)
|
|
216
|
+
c3d[frag] -= mean[None, :]
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def get_which_frag(frags):
|
|
220
|
+
which_frag = dict()
|
|
221
|
+
for frag_ind, frag in enumerate(frags):
|
|
222
|
+
which_frag.update({atom_ind: frag_ind for atom_ind in frag})
|
|
223
|
+
return which_frag
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def form_A(frags, which_frag, formed_bonds):
|
|
227
|
+
"""Construct the A-matrices.
|
|
228
|
+
|
|
229
|
+
AR[(m, n)] (AP[(m, n)]) contains the subset of atoms in Rm (Pm) that forms
|
|
230
|
+
bonds with Rn (Pn).
|
|
231
|
+
"""
|
|
232
|
+
A = dict()
|
|
233
|
+
for m, n in formed_bonds:
|
|
234
|
+
key = (which_frag[m], which_frag[n])
|
|
235
|
+
A.setdefault(key, list()).append(m)
|
|
236
|
+
A.setdefault(key[::-1], list()).append(n)
|
|
237
|
+
return A
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def rotate_inplace(frags, union, bonds):
|
|
241
|
+
# which_frag could also be calculated outside of this function
|
|
242
|
+
which_frag = dict()
|
|
243
|
+
for i, frag in enumerate(frags):
|
|
244
|
+
for ind in frag:
|
|
245
|
+
which_frag[ind] = i
|
|
246
|
+
AR = form_A(frags, which_frag, bonds)
|
|
247
|
+
|
|
248
|
+
def form_G(A):
|
|
249
|
+
G = dict()
|
|
250
|
+
for (m, n), inds in A.items():
|
|
251
|
+
G.setdefault(m, set())
|
|
252
|
+
G[m] |= set(inds)
|
|
253
|
+
|
|
254
|
+
for k, v in G.items():
|
|
255
|
+
G[k] = list(v)
|
|
256
|
+
assert len(v) > 0
|
|
257
|
+
return G
|
|
258
|
+
|
|
259
|
+
GR = form_G(AR)
|
|
260
|
+
|
|
261
|
+
# Rotate R fragments
|
|
262
|
+
alphas = get_steps_to_active_atom_mean(frags, frags, AR, union.coords3d)
|
|
263
|
+
gammas = np.zeros_like(alphas)
|
|
264
|
+
for m, rfrag in enumerate(frags):
|
|
265
|
+
Gm = GR[m]
|
|
266
|
+
gammas[m] = union.coords3d[Gm].mean(axis=0)
|
|
267
|
+
r_means = np.array([union.coords3d[frag].mean(axis=0) for frag in frags])
|
|
268
|
+
|
|
269
|
+
for m, rfrag in enumerate(frags):
|
|
270
|
+
gm = r_means[m]
|
|
271
|
+
rot_mat = get_rot_mat(gammas[m] - gm, alphas[m] - gm)
|
|
272
|
+
rot_coords = (union.coords3d[rfrag] - gm).dot(rot_mat)
|
|
273
|
+
union.coords3d[rfrag] = rot_coords + gm - rot_coords.mean(axis=0)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
CONFIG = {
|
|
277
|
+
"s2_hs_kappa": 1.0,
|
|
278
|
+
"s4_hs_kappa": 50.0,
|
|
279
|
+
"s4_v_kappa": 1.0,
|
|
280
|
+
"s4_w_kappa": 1.0,
|
|
281
|
+
"s5_v_kappa": 1.0,
|
|
282
|
+
"s5_w_kappa": 3.0,
|
|
283
|
+
"s5_hs_kappa": 10.0,
|
|
284
|
+
"s5_z_kappa": 2.0,
|
|
285
|
+
"s5_trans": True,
|
|
286
|
+
"s5_rms_force": 0.01,
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def precon_pos_rot(reactants, products, prefix=None, config=CONFIG):
|
|
291
|
+
c = config
|
|
292
|
+
|
|
293
|
+
if prefix is None:
|
|
294
|
+
prefix = ""
|
|
295
|
+
|
|
296
|
+
def make_fn(fn):
|
|
297
|
+
return prefix + fn
|
|
298
|
+
|
|
299
|
+
rfrags, rfrag_bonds, rbonds, runion = get_fragments_and_bonds(reactants)
|
|
300
|
+
pfrags, pfrag_bonds, pbonds, punion = get_fragments_and_bonds(products)
|
|
301
|
+
|
|
302
|
+
pbond_diff = pbonds - rbonds # Present in product(s)
|
|
303
|
+
rbond_diff = rbonds - pbonds # Present in reactant(s)
|
|
304
|
+
involved_atoms = set(tuple(it.chain(*pbond_diff)))
|
|
305
|
+
involved_atoms |= set(tuple(it.chain(*rbond_diff)))
|
|
306
|
+
|
|
307
|
+
which_rfrag = get_which_frag(rfrags)
|
|
308
|
+
which_pfrag = get_which_frag(pfrags)
|
|
309
|
+
|
|
310
|
+
rfrag_lists = [list(frag) for frag in rfrags]
|
|
311
|
+
pfrag_lists = [list(frag) for frag in pfrags]
|
|
312
|
+
|
|
313
|
+
report_frags(runion, punion, rfrags, pfrags, rbond_diff, pbond_diff)
|
|
314
|
+
|
|
315
|
+
def form_C(m_frags, n_frags):
|
|
316
|
+
"""Construct the C-matrices.
|
|
317
|
+
|
|
318
|
+
Returns a dict with (m, n) keys, containing the respective
|
|
319
|
+
unions of rectant fragment n and product fragment m.
|
|
320
|
+
"""
|
|
321
|
+
C = dict()
|
|
322
|
+
for m, m_frag in enumerate(m_frags):
|
|
323
|
+
for n, n_frag in enumerate(n_frags):
|
|
324
|
+
C[(m, n)] = list(m_frag & n_frag)
|
|
325
|
+
return C
|
|
326
|
+
|
|
327
|
+
CR = form_C(rfrags, pfrags)
|
|
328
|
+
assert len(set(it.chain(*CR.values()))) == len(runion.atoms)
|
|
329
|
+
CP = {(n, m): union for (m, n), union in CR.items()}
|
|
330
|
+
print("CR(m, n), subset of atoms in molecule Rn which are in Pm after reaction.")
|
|
331
|
+
report_mats("CR", CR)
|
|
332
|
+
print("CP(m, n), subset of atoms in molecule Pn which are in Rm before reaction.")
|
|
333
|
+
report_mats("CP", CP)
|
|
334
|
+
|
|
335
|
+
def form_B(C):
|
|
336
|
+
"""Construct the B-matrices.
|
|
337
|
+
|
|
338
|
+
Returns a dict with (m, n) keys, containing the respective
|
|
339
|
+
subsets of C[(m, n)] that acutally participate in bond-breaking/forming.
|
|
340
|
+
"""
|
|
341
|
+
B = dict()
|
|
342
|
+
for (m, n), union in C.items():
|
|
343
|
+
key = (m, n)
|
|
344
|
+
B.setdefault(key, set())
|
|
345
|
+
B[key] |= set(union) & involved_atoms
|
|
346
|
+
for k, v in B.items():
|
|
347
|
+
B[k] = list(v)
|
|
348
|
+
return B
|
|
349
|
+
|
|
350
|
+
BR = form_B(CR)
|
|
351
|
+
BP = form_B(CP)
|
|
352
|
+
print(
|
|
353
|
+
"BR(m, n), subset of atoms in CRnm actually involved in bond forming/breaking."
|
|
354
|
+
)
|
|
355
|
+
report_mats("BR", BR)
|
|
356
|
+
print(
|
|
357
|
+
"BP(m, n), subset of atoms in CPnm actually involved in bond forming/breaking."
|
|
358
|
+
)
|
|
359
|
+
report_mats("BP", BP)
|
|
360
|
+
|
|
361
|
+
AR = form_A(rfrags, which_rfrag, pbond_diff)
|
|
362
|
+
AP = form_A(pfrags, which_pfrag, rbond_diff)
|
|
363
|
+
print("AR(m, n), subset of atoms in Rm that form bonds to atoms in Rn.")
|
|
364
|
+
report_mats("AR", AR)
|
|
365
|
+
print(
|
|
366
|
+
"AP(m, n), subset of atoms in Pm which had bonds with Pn (formerly bonded in R)."
|
|
367
|
+
)
|
|
368
|
+
report_mats("AP", AP)
|
|
369
|
+
|
|
370
|
+
def form_G(A):
|
|
371
|
+
G = dict()
|
|
372
|
+
for (m, n), inds in A.items():
|
|
373
|
+
G.setdefault(m, set())
|
|
374
|
+
G[m] |= set(inds)
|
|
375
|
+
|
|
376
|
+
for k, v in G.items():
|
|
377
|
+
G[k] = list(v)
|
|
378
|
+
assert len(v) > 0
|
|
379
|
+
return G
|
|
380
|
+
|
|
381
|
+
GR = form_G(AR)
|
|
382
|
+
# GP = form_G(AP)
|
|
383
|
+
print(f"GR: {GR}")
|
|
384
|
+
# print(f"GP: {GP}")
|
|
385
|
+
|
|
386
|
+
# Initial, centered, coordinates and 5 stages
|
|
387
|
+
r_coords = np.zeros((6, runion.coords.size))
|
|
388
|
+
p_coords = np.zeros((6, punion.coords.size))
|
|
389
|
+
|
|
390
|
+
def backup_coords(stage):
|
|
391
|
+
assert 0 <= stage < 6
|
|
392
|
+
r_coords[stage] = runion.coords.copy()
|
|
393
|
+
p_coords[stage] = punion.coords.copy()
|
|
394
|
+
|
|
395
|
+
"""
|
|
396
|
+
STAGE 1
|
|
397
|
+
Initial positioning of reactant and product molecules
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
# Center fragments at their geometric average
|
|
401
|
+
center_fragments(rfrag_lists, runion)
|
|
402
|
+
center_fragments(pfrag_lists, punion)
|
|
403
|
+
|
|
404
|
+
backup_coords(0)
|
|
405
|
+
|
|
406
|
+
# Translate reactant molecules
|
|
407
|
+
alphas = get_steps_to_active_atom_mean(
|
|
408
|
+
rfrag_lists, rfrag_lists, AR, runion.coords3d
|
|
409
|
+
)
|
|
410
|
+
for rfrag, alpha in zip(rfrag_lists, alphas):
|
|
411
|
+
runion.coords3d[rfrag] += alpha
|
|
412
|
+
|
|
413
|
+
# Translate product molecules
|
|
414
|
+
betas = get_steps_to_active_atom_mean(
|
|
415
|
+
pfrag_lists, rfrag_lists, BR, punion.coords3d, skip=False
|
|
416
|
+
)
|
|
417
|
+
sigmas = get_steps_to_active_atom_mean(
|
|
418
|
+
pfrag_lists, rfrag_lists, CR, punion.coords3d, skip=False
|
|
419
|
+
)
|
|
420
|
+
bs_half = (betas + sigmas) / 2
|
|
421
|
+
for pfrag, bsh in zip(pfrag_lists, bs_half):
|
|
422
|
+
punion.coords3d[pfrag] += bsh
|
|
423
|
+
|
|
424
|
+
backup_coords(1)
|
|
425
|
+
print()
|
|
426
|
+
|
|
427
|
+
"""
|
|
428
|
+
STAGE 2
|
|
429
|
+
Intra-image Inter-molecular Hard-Sphere forces
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
print(highlight_text("Stage 2, Hard-Sphere Forces"))
|
|
433
|
+
|
|
434
|
+
s2_hs_kappa = c["s2_hs_kappa"]
|
|
435
|
+
|
|
436
|
+
def hardsphere_sd_opt(geom, frag_lists, title):
|
|
437
|
+
print(highlight_text(title, level=1))
|
|
438
|
+
calc = HardSphere(geom, frag_lists, kappa=s2_hs_kappa)
|
|
439
|
+
geom.set_calculator(calc)
|
|
440
|
+
opt_kwargs = {
|
|
441
|
+
"max_cycles": 1000,
|
|
442
|
+
"max_step": 0.5,
|
|
443
|
+
"rms_force": 0.05,
|
|
444
|
+
}
|
|
445
|
+
opt = SteepestDescent(geom, **opt_kwargs)
|
|
446
|
+
opt.run()
|
|
447
|
+
|
|
448
|
+
hardsphere_sd_opt(runion, rfrag_lists, "Reactants")
|
|
449
|
+
hardsphere_sd_opt(punion, pfrag_lists, "Products")
|
|
450
|
+
|
|
451
|
+
backup_coords(2)
|
|
452
|
+
print()
|
|
453
|
+
|
|
454
|
+
"""
|
|
455
|
+
STAGE 3
|
|
456
|
+
Initial orientation of molecules
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
print(highlight_text("Stage 3, Initial Orientation"))
|
|
460
|
+
|
|
461
|
+
# Rotate R fragments
|
|
462
|
+
# TODO: refactor to use rotate_inplace()
|
|
463
|
+
if len(rfrag_lists) > 1:
|
|
464
|
+
alphas = get_steps_to_active_atom_mean(
|
|
465
|
+
rfrag_lists, rfrag_lists, AR, runion.coords3d
|
|
466
|
+
)
|
|
467
|
+
gammas = np.zeros_like(alphas)
|
|
468
|
+
for m, rfrag in enumerate(rfrag_lists):
|
|
469
|
+
Gm = GR[m]
|
|
470
|
+
gammas[m] = runion.coords3d[Gm].mean(axis=0)
|
|
471
|
+
r_means = np.array([runion.coords3d[frag].mean(axis=0) for frag in rfrag_lists])
|
|
472
|
+
|
|
473
|
+
for m, rfrag in enumerate(rfrag_lists):
|
|
474
|
+
gm = r_means[m]
|
|
475
|
+
rot_mat = get_rot_mat(gammas[m] - gm, alphas[m] - gm)
|
|
476
|
+
rot_coords = (runion.coords3d[rfrag] - gm).dot(rot_mat)
|
|
477
|
+
runion.coords3d[rfrag] = rot_coords + gm - rot_coords.mean(axis=0)
|
|
478
|
+
|
|
479
|
+
Ns = [0] * len(pfrag_lists)
|
|
480
|
+
for (m, n), CPmn in CP.items():
|
|
481
|
+
Ns[m] += len(CPmn)
|
|
482
|
+
|
|
483
|
+
# Rotate P fragments
|
|
484
|
+
for m, pfrag in enumerate(pfrag_lists):
|
|
485
|
+
pc3d = punion.coords3d[pfrag]
|
|
486
|
+
gm = pc3d.mean(axis=0)
|
|
487
|
+
r0Pm = pc3d - gm[None, :]
|
|
488
|
+
mu_Pm = np.zeros_like(r0Pm)
|
|
489
|
+
N = Ns[m]
|
|
490
|
+
for n, rfrag in enumerate(rfrag_lists):
|
|
491
|
+
# Skip rotation of 1-atom fragments
|
|
492
|
+
if len(rfrag) == 1:
|
|
493
|
+
continue
|
|
494
|
+
CPmn = CP[(m, n)]
|
|
495
|
+
RPmRn = get_rot_mat(
|
|
496
|
+
punion.coords3d[CPmn], runion.coords3d[CPmn], center=True
|
|
497
|
+
)
|
|
498
|
+
print(f"m={m}, n={n}, len(CPmn)={len(CPmn)}")
|
|
499
|
+
# Eq. (A2) in [1]
|
|
500
|
+
r0Pmn = np.einsum("ij,jk->ki", RPmRn, r0Pm.T)
|
|
501
|
+
mu_Pm += len(CPmn) ** 2 / N * r0Pmn
|
|
502
|
+
rot_mat = get_rot_mat(r0Pm, mu_Pm, center=True)
|
|
503
|
+
rot_coords = r0Pm.dot(rot_mat)
|
|
504
|
+
punion.coords3d[pfrag] = rot_coords + gm - rot_coords.mean(axis=0)
|
|
505
|
+
|
|
506
|
+
backup_coords(3)
|
|
507
|
+
print()
|
|
508
|
+
|
|
509
|
+
"""
|
|
510
|
+
STAGE 4
|
|
511
|
+
Alignment of reactive atoms
|
|
512
|
+
|
|
513
|
+
This stage involves three forces: hard-sphere forces and two kinds
|
|
514
|
+
of average translational (^t) and rotational (^r) forces (v and w,
|
|
515
|
+
(A3) - (A5) in [1]).
|
|
516
|
+
|
|
517
|
+
v^t and v^r arise from atoms in A^Rnm and A^Rmn, that is atoms that
|
|
518
|
+
participate in bond forming/breaking in R. The translational force
|
|
519
|
+
is usually attractive, which is counteracted by the repulsive hard-sphere
|
|
520
|
+
forces.
|
|
521
|
+
"""
|
|
522
|
+
|
|
523
|
+
print(highlight_text("Stage 4, Alignment Of Reactive Atoms"))
|
|
524
|
+
|
|
525
|
+
def composite_sd_opt(geom, keys_calcs, title, rms_force=0.05):
|
|
526
|
+
print(highlight_text(title, level=1))
|
|
527
|
+
final = " + ".join([k for k in keys_calcs.keys()])
|
|
528
|
+
calc = Composite(final, keys_calcs=keys_calcs)
|
|
529
|
+
geom.set_calculator(calc)
|
|
530
|
+
opt_kwargs = {
|
|
531
|
+
"max_step": 0.05,
|
|
532
|
+
"max_cycles": 2000,
|
|
533
|
+
"rms_force": rms_force,
|
|
534
|
+
}
|
|
535
|
+
opt = SteepestDescent(geom, **opt_kwargs)
|
|
536
|
+
opt.run()
|
|
537
|
+
|
|
538
|
+
def get_vr_trans_torque(kappa=1.0, do_trans=True):
|
|
539
|
+
return TransTorque(
|
|
540
|
+
rfrag_lists, rfrag_lists, AR, AR, kappa=kappa, do_trans=do_trans
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
def r_weight_func(m, n, a, b):
|
|
544
|
+
"""As required for (A5) in [1]."""
|
|
545
|
+
return 1 if a in BR[(m, n)] else 0.5
|
|
546
|
+
|
|
547
|
+
def get_wr_trans_torque(kappa=1.0, do_trans=True):
|
|
548
|
+
return TransTorque(
|
|
549
|
+
rfrag_lists,
|
|
550
|
+
pfrag_lists,
|
|
551
|
+
CR,
|
|
552
|
+
CP,
|
|
553
|
+
weight_func=r_weight_func,
|
|
554
|
+
skip=False,
|
|
555
|
+
b_coords3d=punion.coords3d,
|
|
556
|
+
kappa=kappa,
|
|
557
|
+
do_trans=do_trans,
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
def get_vp_trans_torque(kappa=1.0, do_trans=True):
|
|
561
|
+
return TransTorque(
|
|
562
|
+
pfrag_lists, pfrag_lists, AP, AP, kappa=kappa, do_trans=do_trans
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
def p_weight_func(m, n, a, b):
|
|
566
|
+
"""As required for (A5) in [1]."""
|
|
567
|
+
return 1 if a in BP[(m, n)] else 0.5
|
|
568
|
+
|
|
569
|
+
def get_wp_trans_torque(kappa=1.0, do_trans=True):
|
|
570
|
+
return TransTorque(
|
|
571
|
+
pfrag_lists,
|
|
572
|
+
rfrag_lists,
|
|
573
|
+
CP,
|
|
574
|
+
CR,
|
|
575
|
+
weight_func=p_weight_func,
|
|
576
|
+
skip=False,
|
|
577
|
+
b_coords3d=runion.coords3d,
|
|
578
|
+
kappa=kappa,
|
|
579
|
+
do_trans=do_trans,
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
s4_hs_kappa = c["s4_hs_kappa"]
|
|
583
|
+
s4_v_kappa = c["s4_v_kappa"]
|
|
584
|
+
s4_w_kappa = c["s4_w_kappa"]
|
|
585
|
+
|
|
586
|
+
vr_trans_torque = get_vr_trans_torque(kappa=s4_v_kappa)
|
|
587
|
+
wr_trans_torque = get_wr_trans_torque(kappa=s4_w_kappa)
|
|
588
|
+
r_keys_calcs = {
|
|
589
|
+
"hardsphere": HardSphere(runion, rfrag_lists, kappa=s4_hs_kappa),
|
|
590
|
+
"v": vr_trans_torque,
|
|
591
|
+
"w": wr_trans_torque,
|
|
592
|
+
}
|
|
593
|
+
composite_sd_opt(runion, r_keys_calcs, "Reactants")
|
|
594
|
+
|
|
595
|
+
vp_trans_torque = get_vp_trans_torque(kappa=s4_v_kappa)
|
|
596
|
+
wp_trans_torque = get_wp_trans_torque(kappa=s4_w_kappa)
|
|
597
|
+
p_keys_calcs = {
|
|
598
|
+
"hardsphere": HardSphere(punion, pfrag_lists, kappa=s4_hs_kappa),
|
|
599
|
+
"v": vp_trans_torque,
|
|
600
|
+
"w": wp_trans_torque,
|
|
601
|
+
}
|
|
602
|
+
composite_sd_opt(punion, p_keys_calcs, "Products")
|
|
603
|
+
|
|
604
|
+
backup_coords(4)
|
|
605
|
+
print()
|
|
606
|
+
|
|
607
|
+
"""
|
|
608
|
+
STAGE 5
|
|
609
|
+
Refinement of atomic positions using further hard-sphere forces.
|
|
610
|
+
"""
|
|
611
|
+
|
|
612
|
+
print(highlight_text("Stage 5, Refinement"))
|
|
613
|
+
|
|
614
|
+
s5_v_kappa = c["s5_v_kappa"]
|
|
615
|
+
s5_w_kappa = c["s5_w_kappa"]
|
|
616
|
+
s5_hs_kappa = c["s5_hs_kappa"]
|
|
617
|
+
s5_z_kappa = c["s5_z_kappa"]
|
|
618
|
+
s5_trans = c["s5_trans"]
|
|
619
|
+
s5_rms_force = c["s5_rms_force"]
|
|
620
|
+
|
|
621
|
+
vr_trans_torque = get_vr_trans_torque(kappa=s5_v_kappa, do_trans=s5_trans)
|
|
622
|
+
wr_trans_torque = get_wr_trans_torque(kappa=s5_w_kappa, do_trans=s5_trans)
|
|
623
|
+
zr_aa_trans_torque = AtomAtomTransTorque(runion, rfrag_lists, AR, kappa=s5_z_kappa)
|
|
624
|
+
r_keys_calcs = {
|
|
625
|
+
"v": vr_trans_torque,
|
|
626
|
+
"w": wr_trans_torque,
|
|
627
|
+
"hardsphere": HardSphere(runion, rfrag_lists, kappa=s5_hs_kappa),
|
|
628
|
+
"z": zr_aa_trans_torque,
|
|
629
|
+
}
|
|
630
|
+
composite_sd_opt(runion, r_keys_calcs, "Reactants", rms_force=s5_rms_force)
|
|
631
|
+
|
|
632
|
+
vp_trans_torque = get_vp_trans_torque(kappa=s5_v_kappa, do_trans=s5_trans)
|
|
633
|
+
wp_trans_torque = get_wp_trans_torque(kappa=s5_w_kappa, do_trans=s5_trans)
|
|
634
|
+
zp_aa_trans_torque = AtomAtomTransTorque(punion, pfrag_lists, AP, kappa=s5_z_kappa)
|
|
635
|
+
p_keys_calcs = {
|
|
636
|
+
"v": vp_trans_torque,
|
|
637
|
+
"w": wp_trans_torque,
|
|
638
|
+
"hardsphere": HardSphere(punion, pfrag_lists, kappa=s5_hs_kappa),
|
|
639
|
+
"z": zp_aa_trans_torque,
|
|
640
|
+
}
|
|
641
|
+
composite_sd_opt(punion, p_keys_calcs, "Products", rms_force=s5_rms_force)
|
|
642
|
+
|
|
643
|
+
backup_coords(5)
|
|
644
|
+
print()
|
|
645
|
+
|
|
646
|
+
with open(make_fn("s5_trj.xyz"), "w") as handle:
|
|
647
|
+
handle.write("\n".join([geom.as_xyz() for geom in (runion, punion)]))
|
|
648
|
+
|
|
649
|
+
def dump_stages(fn, atoms, coords_list):
|
|
650
|
+
align_coords(coords_list)
|
|
651
|
+
comments = [f"Stage {i}" for i in range(coords_list.shape[0])]
|
|
652
|
+
fn = make_fn(fn)
|
|
653
|
+
coords_to_trj(fn, atoms, coords_list, comments=comments)
|
|
654
|
+
|
|
655
|
+
dump_stages("r_coords_trj.xyz", runion.atoms, r_coords)
|
|
656
|
+
dump_stages("p_coords_trj.xyz", punion.atoms, p_coords)
|
|
657
|
+
|
|
658
|
+
runion.set_calculator(None)
|
|
659
|
+
punion.set_calculator(None)
|
|
660
|
+
return runion, punion
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def run_precontr(reactant_geom, product_geom, **kwargs):
|
|
664
|
+
print(
|
|
665
|
+
highlight_text("Preconditioning of Translation & Rotation")
|
|
666
|
+
+ "\n\nPlease cite https://doi.org/10.1002/jcc.26495\n"
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
return precon_pos_rot(reactant_geom, product_geom, **kwargs)
|