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,364 @@
|
|
|
1
|
+
# [1] https://doi.org/10.1063/1.5082885
|
|
2
|
+
# Minimum dynamic path
|
|
3
|
+
# Unke, 2019
|
|
4
|
+
|
|
5
|
+
from collections import namedtuple
|
|
6
|
+
import operator
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from pysisyphus.constants import AU2KJPERMOL
|
|
12
|
+
from pysisyphus.dynamics.driver import md, MDResult
|
|
13
|
+
from pysisyphus.dynamics.helpers import (
|
|
14
|
+
dump_coords,
|
|
15
|
+
get_mb_velocities_for_geom,
|
|
16
|
+
temperature_for_kinetic_energy,
|
|
17
|
+
)
|
|
18
|
+
from pysisyphus.helpers_pure import highlight_text
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parse_raw_term_func(raw_term_func):
|
|
22
|
+
funcs = {
|
|
23
|
+
"<": operator.lt,
|
|
24
|
+
">": operator.gt,
|
|
25
|
+
"<=": operator.le,
|
|
26
|
+
">=": operator.ge,
|
|
27
|
+
"==": operator.eq,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def comp_closure(indices, op, ref_value):
|
|
31
|
+
a_ind, b_ind = indices
|
|
32
|
+
func = funcs[op]
|
|
33
|
+
|
|
34
|
+
def comp_func(coords3d):
|
|
35
|
+
a = coords3d[a_ind]
|
|
36
|
+
b = coords3d[b_ind]
|
|
37
|
+
dist = np.linalg.norm(a - b)
|
|
38
|
+
return func(dist, ref_value)
|
|
39
|
+
|
|
40
|
+
return comp_func
|
|
41
|
+
|
|
42
|
+
operator_re = re.compile("([<>=]+)")
|
|
43
|
+
mobj = operator_re.split(raw_term_func)
|
|
44
|
+
if mobj is None:
|
|
45
|
+
print(f"Could not parse term_func '{raw_term_func}!'")
|
|
46
|
+
return None
|
|
47
|
+
indices, op, ref_value = mobj
|
|
48
|
+
ref_value = float(ref_value)
|
|
49
|
+
indices = [int(ind) for ind in indices.split(",")]
|
|
50
|
+
return comp_closure(indices, op, ref_value)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def parse_raw_term_funcs(raw_term_funcs):
|
|
54
|
+
comp_funcs = {}
|
|
55
|
+
for k, v in raw_term_funcs.items():
|
|
56
|
+
comp_func = parse_raw_term_func(v)
|
|
57
|
+
if comp_func:
|
|
58
|
+
comp_funcs[k] = comp_func
|
|
59
|
+
return comp_funcs
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def run_md(geom, dt, steps, v0=None, term_funcs=None, external=False):
|
|
63
|
+
if external and hasattr(geom.calculator, "run_md"):
|
|
64
|
+
t = dt * steps
|
|
65
|
+
t_ps = t * 1e-3
|
|
66
|
+
md_kwargs = {
|
|
67
|
+
"atoms": geom.atoms,
|
|
68
|
+
"coords": geom.coords,
|
|
69
|
+
"t": t,
|
|
70
|
+
"dt": dt,
|
|
71
|
+
"velocities": v0,
|
|
72
|
+
"dump": dt,
|
|
73
|
+
}
|
|
74
|
+
print("Running MD with external calculator implementation.")
|
|
75
|
+
if term_funcs is not None:
|
|
76
|
+
print("Termination functions are not supported in external MD!")
|
|
77
|
+
geoms = geom.calculator.run_md(**md_kwargs)
|
|
78
|
+
md_result = MDResult(
|
|
79
|
+
coords=[geom.coords for geom in geoms],
|
|
80
|
+
t_ps=t_ps,
|
|
81
|
+
step=int(t / dt - 1),
|
|
82
|
+
terminated=None,
|
|
83
|
+
T=None,
|
|
84
|
+
E_tot=None,
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
md_kwargs = {
|
|
88
|
+
"v0": v0,
|
|
89
|
+
"steps": steps,
|
|
90
|
+
"dt": dt,
|
|
91
|
+
"term_funcs": term_funcs,
|
|
92
|
+
"verbose": False,
|
|
93
|
+
"remove_com_v": False,
|
|
94
|
+
}
|
|
95
|
+
print("Running MD with internal implementation.")
|
|
96
|
+
md_result = md(geom, **md_kwargs)
|
|
97
|
+
|
|
98
|
+
return md_result
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
MDPResult = namedtuple(
|
|
102
|
+
"MDResult",
|
|
103
|
+
"ascent_xs md_init_plus md_init_minus "
|
|
104
|
+
"md_fin_plus md_fin_minus "
|
|
105
|
+
"md_fin_plus_term md_fin_minus_term",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def mdp(
|
|
110
|
+
geom,
|
|
111
|
+
steps,
|
|
112
|
+
dt,
|
|
113
|
+
term_funcs=None,
|
|
114
|
+
steps_init=None,
|
|
115
|
+
E_excess=0.0,
|
|
116
|
+
displ_length=0.1,
|
|
117
|
+
epsilon=5e-4,
|
|
118
|
+
ascent_alpha=0.05,
|
|
119
|
+
max_ascent_steps=25,
|
|
120
|
+
max_init_trajs=10,
|
|
121
|
+
dump=True,
|
|
122
|
+
seed=None,
|
|
123
|
+
external_md=False,
|
|
124
|
+
):
|
|
125
|
+
# Sanity checks and forcing some types
|
|
126
|
+
dt = float(dt)
|
|
127
|
+
assert dt > 0.0
|
|
128
|
+
steps = int(steps)
|
|
129
|
+
t = dt * steps
|
|
130
|
+
# assert t > dt
|
|
131
|
+
if steps_init is None:
|
|
132
|
+
steps_init = steps // 10
|
|
133
|
+
print(f"No 'steps_init' provided! Using {steps_init}")
|
|
134
|
+
E_excess = float(E_excess)
|
|
135
|
+
assert E_excess >= 0.0
|
|
136
|
+
displ_length = float(displ_length)
|
|
137
|
+
assert displ_length >= 0.0
|
|
138
|
+
if term_funcs is None:
|
|
139
|
+
term_funcs = {}
|
|
140
|
+
for k, v in term_funcs.items():
|
|
141
|
+
if callable(v):
|
|
142
|
+
continue
|
|
143
|
+
elif isinstance(v, str):
|
|
144
|
+
term_funcs[k] = parse_raw_term_func(v)
|
|
145
|
+
else:
|
|
146
|
+
raise Exception(f"Invalid term function '{k}: {v}' encountered!")
|
|
147
|
+
|
|
148
|
+
print(highlight_text("Minimum dynamic path calculation"))
|
|
149
|
+
|
|
150
|
+
if seed is None:
|
|
151
|
+
# 2**32 - 1
|
|
152
|
+
seed = np.random.randint(4294967295)
|
|
153
|
+
np.random.seed(seed)
|
|
154
|
+
print(f"Using seed {seed} to initialize the random number generator.\n")
|
|
155
|
+
|
|
156
|
+
E_TS = geom.energy
|
|
157
|
+
E_tot = E_TS + E_excess
|
|
158
|
+
# Distribute E_excess evenly on E_pot and E_kin
|
|
159
|
+
E_pot_diff = 0.5 * E_excess
|
|
160
|
+
E_pot_desired = E_TS + E_pot_diff
|
|
161
|
+
|
|
162
|
+
print(f"E_TS={E_TS:.6f} au")
|
|
163
|
+
|
|
164
|
+
# Determine transition vector
|
|
165
|
+
w, v = np.linalg.eigh(geom.hessian)
|
|
166
|
+
assert w[0] < -1e-8
|
|
167
|
+
trans_vec = v[:, 0]
|
|
168
|
+
|
|
169
|
+
# Disable removal of translation/rotation for analytical potentials
|
|
170
|
+
remove_com_v = remove_rot_v = geom.cart_coords.size > 3
|
|
171
|
+
|
|
172
|
+
if E_excess == 0.0:
|
|
173
|
+
print("MDP without excess energy.")
|
|
174
|
+
# Without excess energy we have to do an initial displacement along
|
|
175
|
+
# the transition vector to get a non-vanishing gradient.
|
|
176
|
+
initial_displacement = displ_length * trans_vec
|
|
177
|
+
x0_plus = geom.coords + initial_displacement
|
|
178
|
+
x0_minus = geom.coords - initial_displacement
|
|
179
|
+
|
|
180
|
+
v0_zero = np.zeros_like(geom.coords)
|
|
181
|
+
md_kwargs = {
|
|
182
|
+
"v0": v0_zero.copy(),
|
|
183
|
+
"t": t,
|
|
184
|
+
"dt": dt,
|
|
185
|
+
"term_funcs": term_funcs,
|
|
186
|
+
"external": external_md,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
geom.coords = x0_plus
|
|
190
|
+
md_fin_plus = run_md(geom, **md_kwargs)
|
|
191
|
+
|
|
192
|
+
geom.coords = x0_minus
|
|
193
|
+
md_fin_minus = run_md(geom, **md_kwargs)
|
|
194
|
+
|
|
195
|
+
if dump:
|
|
196
|
+
dump_coords(geom.atoms, md_fin_plus.coords, "mdp_plus_trj.xyz")
|
|
197
|
+
dump_coords(geom.atoms, md_fin_minus.coords, "mdp_minus_trj.xyz")
|
|
198
|
+
|
|
199
|
+
mdp_result = MDPResult(
|
|
200
|
+
ascent_xs=None,
|
|
201
|
+
md_init_plus=None,
|
|
202
|
+
md_init_minus=None,
|
|
203
|
+
md_fin_plus=md_fin_plus,
|
|
204
|
+
md_fin_minus=md_fin_minus,
|
|
205
|
+
)
|
|
206
|
+
return mdp_result
|
|
207
|
+
|
|
208
|
+
print(f"E_excess={E_excess:.6f} au, ({E_excess*AU2KJPERMOL:.1f} kJ/mol)")
|
|
209
|
+
print(f"E_pot,desired=E_TS + {E_pot_diff*AU2KJPERMOL:.1f} kJ/mol")
|
|
210
|
+
print()
|
|
211
|
+
|
|
212
|
+
# Generate random vector perpendicular to transition vector
|
|
213
|
+
perp_vec = np.random.rand(*trans_vec.shape)
|
|
214
|
+
# Zero last element if we have an analytical surface
|
|
215
|
+
if perp_vec.size == 3:
|
|
216
|
+
perp_vec[2] = 0
|
|
217
|
+
# Orthogonalize vector
|
|
218
|
+
perp_vec = perp_vec - (perp_vec @ trans_vec) * trans_vec
|
|
219
|
+
perp_vec /= np.linalg.norm(perp_vec)
|
|
220
|
+
|
|
221
|
+
# Initial displacement from x_TS to x, generating a point with
|
|
222
|
+
# non-vanishing gradient.
|
|
223
|
+
x = geom.coords + epsilon * perp_vec
|
|
224
|
+
geom.coords = x
|
|
225
|
+
|
|
226
|
+
# Do steepest ascent until E_tot is reached
|
|
227
|
+
E_pot = geom.energy
|
|
228
|
+
ascent_xs = list()
|
|
229
|
+
for i in range(max_ascent_steps):
|
|
230
|
+
ascent_xs.append(geom.coords.copy())
|
|
231
|
+
ascent_converged = E_pot >= E_pot_desired
|
|
232
|
+
if ascent_converged:
|
|
233
|
+
break
|
|
234
|
+
gradient = geom.gradient
|
|
235
|
+
E_pot = geom.energy
|
|
236
|
+
|
|
237
|
+
direction = gradient / np.linalg.norm(gradient)
|
|
238
|
+
step = ascent_alpha * direction
|
|
239
|
+
new_coords = geom.coords + step
|
|
240
|
+
geom.coords = new_coords
|
|
241
|
+
|
|
242
|
+
# calc = geom.calculator
|
|
243
|
+
# class Opt:
|
|
244
|
+
# pass
|
|
245
|
+
# _opt = Opt()
|
|
246
|
+
# _opt.coords = np.array(ascent_xs)
|
|
247
|
+
# calc.plot_opt(_opt, show=True)
|
|
248
|
+
|
|
249
|
+
assert ascent_converged, "Steepest ascent didn't converge!"
|
|
250
|
+
assert (E_tot - E_pot) > 0.0, (
|
|
251
|
+
"Potential energy after steepst ascent is greater than the desired "
|
|
252
|
+
f"total energy ({E_pot:.6f} > {E_tot:.6f}). Maybe try a smaller epsilon? "
|
|
253
|
+
f"The current value Ɛ={epsilon:.6f} may be too big!"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
ascent_xs = np.array(ascent_xs)
|
|
257
|
+
if dump:
|
|
258
|
+
dump_coords(geom.atoms, ascent_xs, "mdp_ee_ascent_trj.xyz")
|
|
259
|
+
x0 = geom.coords.copy()
|
|
260
|
+
|
|
261
|
+
print(highlight_text("Runninig initialization trajectories", level=1))
|
|
262
|
+
for i in range(max_init_trajs):
|
|
263
|
+
# Determine random momentum vector for the given kinetic energy
|
|
264
|
+
E_kin = E_tot - E_pot
|
|
265
|
+
T = temperature_for_kinetic_energy(len(geom.atoms), E_kin)
|
|
266
|
+
v0 = get_mb_velocities_for_geom(
|
|
267
|
+
geom, T, remove_com_v=remove_com_v, remove_rot_v=remove_rot_v
|
|
268
|
+
).flatten()
|
|
269
|
+
|
|
270
|
+
# Zero last element if we have an analytical surface
|
|
271
|
+
if v0.size == 3:
|
|
272
|
+
v0[2] = 0
|
|
273
|
+
|
|
274
|
+
# Run initial MD to check if both trajectories run towards different
|
|
275
|
+
# basins of attraction.
|
|
276
|
+
|
|
277
|
+
# First MD with positive v0
|
|
278
|
+
md_init_kwargs = {
|
|
279
|
+
"v0": v0.copy(),
|
|
280
|
+
"steps": steps_init,
|
|
281
|
+
"dt": dt,
|
|
282
|
+
"external": external_md,
|
|
283
|
+
}
|
|
284
|
+
geom.coords = x0.copy()
|
|
285
|
+
md_init_plus = run_md(geom, **md_init_kwargs)
|
|
286
|
+
# Second MD with negative v0
|
|
287
|
+
geom.coords = x0.copy()
|
|
288
|
+
md_init_kwargs["v0"] = -v0.copy()
|
|
289
|
+
md_init_minus = run_md(geom, **md_init_kwargs)
|
|
290
|
+
|
|
291
|
+
dump_coords(geom.atoms, md_init_plus.coords, f"mdp_ee_init_plus_{i:02d}_trj.xyz")
|
|
292
|
+
dump_coords(geom.atoms, md_init_minus.coords, f"mdp_ee_init_minus_{i:02d}_trj.xyz")
|
|
293
|
+
|
|
294
|
+
# Check if both MDs run into different basins of attraction.
|
|
295
|
+
# We (try to) do this by calculating the overlap between the
|
|
296
|
+
# transition vector and the normalized vector defined by the
|
|
297
|
+
# difference between x0 and the endpoint of the respective
|
|
298
|
+
# test trajectory. Both overlaps should have different sings.
|
|
299
|
+
end_plus = md_init_plus.coords[-1]
|
|
300
|
+
pls = end_plus - x0
|
|
301
|
+
pls /= np.linalg.norm(pls)
|
|
302
|
+
end_minus = md_init_minus.coords[-1]
|
|
303
|
+
minus = end_minus - x0
|
|
304
|
+
minus /= np.linalg.norm(minus)
|
|
305
|
+
p = trans_vec @ pls
|
|
306
|
+
m = trans_vec @ minus
|
|
307
|
+
init_trajs_converged = np.sign(p) != np.sign(m)
|
|
308
|
+
|
|
309
|
+
if init_trajs_converged:
|
|
310
|
+
print("Trajectories ran into different basins. Breaking.")
|
|
311
|
+
break
|
|
312
|
+
if dump:
|
|
313
|
+
dump_coords(geom.atoms, md_init_plus.coords, "mdp_ee_init_plus_trj.xyz")
|
|
314
|
+
dump_coords(geom.atoms, md_init_minus.coords, "mdp_ee_init_minus_trj.xyz")
|
|
315
|
+
assert init_trajs_converged
|
|
316
|
+
print(f"Ran 2*{i+1} initialization trajectories.")
|
|
317
|
+
print()
|
|
318
|
+
|
|
319
|
+
# Run actual trajectories, using the supplied termination functions if possible.
|
|
320
|
+
print(highlight_text("Running actual full trajectories.", level=1))
|
|
321
|
+
|
|
322
|
+
def print_status(terminated, step):
|
|
323
|
+
if terminated:
|
|
324
|
+
msg = f"\tTerminated by '{terminated}' in step {step}."
|
|
325
|
+
else:
|
|
326
|
+
msg = "\tMax time steps reached!"
|
|
327
|
+
print(msg)
|
|
328
|
+
|
|
329
|
+
# "Production"/Final MDs
|
|
330
|
+
md_fin_kwargs = {
|
|
331
|
+
"v0": v0.copy(),
|
|
332
|
+
"steps": steps,
|
|
333
|
+
"dt": dt,
|
|
334
|
+
"term_funcs": term_funcs,
|
|
335
|
+
"external": external_md,
|
|
336
|
+
}
|
|
337
|
+
# MD with positive v0.
|
|
338
|
+
geom.coords = x0.copy()
|
|
339
|
+
md_fin_plus = run_md(geom, **md_fin_kwargs)
|
|
340
|
+
print_status(md_fin_plus.terminated, md_fin_plus.step)
|
|
341
|
+
|
|
342
|
+
# MD with negative v0.
|
|
343
|
+
geom.coords = x0.copy()
|
|
344
|
+
md_fin_kwargs["v0"] = -v0
|
|
345
|
+
md_fin_minus = run_md(geom, **md_fin_kwargs)
|
|
346
|
+
print_status(md_fin_minus.terminated, md_fin_minus.step)
|
|
347
|
+
|
|
348
|
+
md_fin_plus_term = md_fin_plus.terminated
|
|
349
|
+
md_fin_minus_term = md_fin_minus.terminated
|
|
350
|
+
|
|
351
|
+
if dump:
|
|
352
|
+
dump_coords(geom.atoms, md_fin_plus.coords, "mdp_ee_fin_plus_trj.xyz")
|
|
353
|
+
dump_coords(geom.atoms, md_fin_minus.coords, "mdp_ee_fin_minus_trj.xyz")
|
|
354
|
+
|
|
355
|
+
mdp_result = MDPResult(
|
|
356
|
+
ascent_xs=ascent_xs,
|
|
357
|
+
md_init_plus=md_init_plus,
|
|
358
|
+
md_init_minus=md_init_minus,
|
|
359
|
+
md_fin_plus=md_fin_plus,
|
|
360
|
+
md_fin_minus=md_fin_minus,
|
|
361
|
+
md_fin_plus_term=md_fin_plus_term,
|
|
362
|
+
md_fin_minus_term=md_fin_minus_term,
|
|
363
|
+
)
|
|
364
|
+
return mdp_result
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pysisyphus.constants import FORCE2ACC
|
|
4
|
+
from pysisyphus.dynamics.helpers import energy_forces_getter_closure, remove_com_velocity
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def rattle_closure(geom, constraints, dt, tol=1e-3, max_cycles=25,
|
|
8
|
+
energy_forces_getter=None, remove_com_v=True):
|
|
9
|
+
# Inverse atom masses
|
|
10
|
+
masses = geom.masses
|
|
11
|
+
inv_masses = 1 / masses
|
|
12
|
+
inv_masses_rep = np.repeat(inv_masses, 3)
|
|
13
|
+
|
|
14
|
+
dt2 = 0.5 * dt
|
|
15
|
+
dt2_sq = dt2 * dt
|
|
16
|
+
tol_sq = tol**2
|
|
17
|
+
|
|
18
|
+
constraints = np.array(constraints, dtype=int)
|
|
19
|
+
inv_masses_sums = inv_masses[constraints[:, 0]] + inv_masses[constraints[:, 1]]
|
|
20
|
+
coords3d = geom.coords3d
|
|
21
|
+
|
|
22
|
+
def get_bond_vecs(coords3d):
|
|
23
|
+
return coords3d[constraints[:, 0]] - coords3d[constraints[:, 1]]
|
|
24
|
+
|
|
25
|
+
# Original bond lengths
|
|
26
|
+
lengths = np.linalg.norm(get_bond_vecs(coords3d), axis=1)
|
|
27
|
+
lengths_sq = lengths**2
|
|
28
|
+
|
|
29
|
+
if energy_forces_getter is None:
|
|
30
|
+
energy_forces_getter = energy_forces_getter_closure(geom)
|
|
31
|
+
# def forces_getter(coords):
|
|
32
|
+
# return geom.get_energy_and_forces_at(coords)["forces"]
|
|
33
|
+
|
|
34
|
+
def rattle(coords, velocities, forces):
|
|
35
|
+
acceleration = forces * inv_masses_rep * FORCE2ACC
|
|
36
|
+
# Bond vectors at time t
|
|
37
|
+
bond_vecs = get_bond_vecs(coords.reshape(-1, 3))
|
|
38
|
+
|
|
39
|
+
# Unconstrained position update
|
|
40
|
+
coords3d_updated = (coords + dt*velocities + dt2_sq * acceleration).reshape(-1, 3)
|
|
41
|
+
# Unconstrained velocity update
|
|
42
|
+
velocities3d_updated = (velocities + dt2 * acceleration).reshape(-1, 3)
|
|
43
|
+
if remove_com_v:
|
|
44
|
+
velocities3d_updated = remove_com_velocity(velocities3d_updated, masses)
|
|
45
|
+
|
|
46
|
+
# First part of RATTLE algorithm.
|
|
47
|
+
# Yields updated positions for t+dt and half-updated velocities for t+dt/2
|
|
48
|
+
for i in range(max_cycles):
|
|
49
|
+
corrected = False
|
|
50
|
+
for j, (atom_1, atom_2) in enumerate(constraints):
|
|
51
|
+
bond_vec_updated = coords3d_updated[atom_1] - coords3d_updated[atom_2]
|
|
52
|
+
bond_length_sq = bond_vec_updated.dot(bond_vec_updated)
|
|
53
|
+
ref_length_sq = lengths_sq[j]
|
|
54
|
+
diff_sq = ref_length_sq - bond_length_sq
|
|
55
|
+
|
|
56
|
+
# We have to correct coordinates and velocities if the deviations is
|
|
57
|
+
# above the tolerance.
|
|
58
|
+
if abs(diff_sq) <= (ref_length_sq * tol_sq):
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
corrected = True
|
|
62
|
+
# Otherwise try to satisfy the constraint by calculating the
|
|
63
|
+
# approximate lagrange multiplier 'g'.
|
|
64
|
+
dot = bond_vec_updated.dot(bond_vecs[j])
|
|
65
|
+
# TODO: test for constraint failure
|
|
66
|
+
g = diff_sq / (2 * inv_masses_sums[j] * dot)
|
|
67
|
+
|
|
68
|
+
# Update positions to satify constraint
|
|
69
|
+
atom_1_factor = g * bond_vecs[j] * inv_masses[atom_1]
|
|
70
|
+
atom_2_factor = g * bond_vecs[j] * inv_masses[atom_2]
|
|
71
|
+
coords3d_updated[atom_1] += atom_1_factor
|
|
72
|
+
coords3d_updated[atom_2] -= atom_2_factor
|
|
73
|
+
# Update velocities to satify constraint
|
|
74
|
+
velocities3d_updated[atom_1] += atom_1_factor / dt
|
|
75
|
+
velocities3d_updated[atom_2] -= atom_2_factor / dt
|
|
76
|
+
|
|
77
|
+
# Stop macro-iterations when no correction was done
|
|
78
|
+
if not corrected:
|
|
79
|
+
# print(f"RATTLE_1 finished after {i+1} macro cycle(s)!")
|
|
80
|
+
break
|
|
81
|
+
else:
|
|
82
|
+
raise Exception("First part of RATTLE did not converge!")
|
|
83
|
+
|
|
84
|
+
# Calculate energy (E_pot) and forces at updated coordinates
|
|
85
|
+
energy_new, forces_new = energy_forces_getter(coords3d_updated.flatten())
|
|
86
|
+
velocities3d_updated += dt2 * (forces_new * inv_masses_rep * FORCE2ACC).reshape(-1, 3)
|
|
87
|
+
if remove_com_v:
|
|
88
|
+
velocities3d_updated = remove_com_velocity(velocities3d_updated, masses)
|
|
89
|
+
# The coordinates aren't updated anymore in the second part, so we can
|
|
90
|
+
# calculate the bond vectors here once and store them.
|
|
91
|
+
bond_vecs = get_bond_vecs(coords3d_updated)
|
|
92
|
+
|
|
93
|
+
# Second part of RATTLE algorithm
|
|
94
|
+
# Update velocities to full time step t+dt
|
|
95
|
+
for i in range(max_cycles):
|
|
96
|
+
corrected = False
|
|
97
|
+
for j, (atom_1, atom_2) in enumerate(constraints):
|
|
98
|
+
velocity_diff = (velocities3d_updated[atom_1]
|
|
99
|
+
- velocities3d_updated[atom_2])
|
|
100
|
+
dot = bond_vecs[j].dot(velocity_diff)
|
|
101
|
+
# Approximate Lagrange multiplier
|
|
102
|
+
k = dot / (inv_masses_sums[j] * lengths_sq[j])
|
|
103
|
+
|
|
104
|
+
if abs(k) <= tol_sq:
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
corrected = True
|
|
108
|
+
# Update velocities to satify constraint
|
|
109
|
+
velocities3d_updated[atom_1] -= k * bond_vecs[j] * inv_masses[atom_1]
|
|
110
|
+
velocities3d_updated[atom_2] += k * bond_vecs[j] * inv_masses[atom_2]
|
|
111
|
+
|
|
112
|
+
# Stop macro-iterations when no correction was done
|
|
113
|
+
if not corrected:
|
|
114
|
+
# print(f"RATTLE_2 finished after {i+1} macro cycle(s)!")
|
|
115
|
+
break
|
|
116
|
+
else:
|
|
117
|
+
raise Exception("Second part of RATTLE did not converge!")
|
|
118
|
+
|
|
119
|
+
return coords3d_updated.flatten(), velocities3d_updated.flatten(), energy_new, forces_new
|
|
120
|
+
|
|
121
|
+
return rattle
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from math import exp, sqrt
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from numpy.random import default_rng
|
|
5
|
+
|
|
6
|
+
from pysisyphus.constants import KBAU
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
[1] https://aip.scitation.org/doi/10.1063/1.2408420
|
|
11
|
+
[2] https://dx.doi.org/10.1016/j.cpc.2008.01.006
|
|
12
|
+
Reformulation fo the algorithm. This is implemented e.g., in YAFF.
|
|
13
|
+
https://github.com/molmod/yaff/blob/master/yaff/sampling/nvt.py
|
|
14
|
+
|
|
15
|
+
csvr_closure() is based on the implementation provided on Bussis homepage:
|
|
16
|
+
https://sites.google.com/site/giovannibussi/downloads/resamplekin.tgz
|
|
17
|
+
(At least in my implementation) there seems to be a problem with
|
|
18
|
+
the conserved quantity, which is not conserved at all ...
|
|
19
|
+
csvr_closure_2() is based on [2]
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
RNG = default_rng()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def sum_noises(num, rng=None):
|
|
27
|
+
"""
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
num : int
|
|
31
|
+
Number of independent Gaussian noises to be squared.
|
|
32
|
+
rng : numpy.random.Generator, optional
|
|
33
|
+
Instances of a random number generator (RNG). If it is not provided the module-level
|
|
34
|
+
RNG will be used.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
if rng is None:
|
|
38
|
+
rng = RNG
|
|
39
|
+
|
|
40
|
+
if num == 0:
|
|
41
|
+
sum_ = 0.0
|
|
42
|
+
elif num == 1:
|
|
43
|
+
sum_ = rng.normal()**2
|
|
44
|
+
# nn even, dof - 1 odd
|
|
45
|
+
elif (num % 2) == 0:
|
|
46
|
+
sum_ = 2.0 * rng.gamma(shape=num/2)
|
|
47
|
+
# nn odd, dof - 1 even
|
|
48
|
+
else:
|
|
49
|
+
sum_ = 2.0 * rng.gamma(shape=(num-1)/2) + rng.normal()**2
|
|
50
|
+
|
|
51
|
+
return sum_
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def csvr_closure(sigma, dof, dt, tau=100, rng=None):
|
|
55
|
+
"""
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
sigma : float
|
|
59
|
+
Target average value of the kinetic energy (1/2 dof k_b T) in the same units as
|
|
60
|
+
cur_kinetic_energy.
|
|
61
|
+
dof : int
|
|
62
|
+
Degrees of freedom.
|
|
63
|
+
tau : float
|
|
64
|
+
Timeconstant of the thermostat. tau : float
|
|
65
|
+
Timeconstant of the thermostat.
|
|
66
|
+
rng : numpy.random.Generator, optional
|
|
67
|
+
Instances of a random number generator (RNG). If it is not provided the module-level
|
|
68
|
+
RNG will be used.
|
|
69
|
+
"""
|
|
70
|
+
# Relaxation time of the thermostat in units of "how often this routine
|
|
71
|
+
# is called" (dt / timeconstant).
|
|
72
|
+
tau_t = dt / tau
|
|
73
|
+
|
|
74
|
+
if tau_t > 0.1:
|
|
75
|
+
factor = exp(-1.0 / tau_t)
|
|
76
|
+
else:
|
|
77
|
+
factor = 0.0
|
|
78
|
+
|
|
79
|
+
if rng is None:
|
|
80
|
+
rng = RNG
|
|
81
|
+
|
|
82
|
+
def resample_kin(cur_kinetic_energy):
|
|
83
|
+
"""
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
cur_kinetic_energy : float
|
|
87
|
+
Present value of the kinetic energy of the atoms to be thermalized
|
|
88
|
+
in arbitrary units.
|
|
89
|
+
"""
|
|
90
|
+
rr = rng.normal()
|
|
91
|
+
new_kinetic_energy = (
|
|
92
|
+
cur_kinetic_energy
|
|
93
|
+
+ (1.0 - factor)
|
|
94
|
+
* (sigma * (sum_noises(dof-1) + rr**2) / dof - cur_kinetic_energy)
|
|
95
|
+
+ 2.0 * rr * sqrt(cur_kinetic_energy * sigma / dof * (1.0 - factor) * factor)
|
|
96
|
+
)
|
|
97
|
+
alpha = sqrt(new_kinetic_energy / cur_kinetic_energy)
|
|
98
|
+
return alpha
|
|
99
|
+
return resample_kin
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def csvr_closure_2(sigma, dof, dt, tau=100, rng=None):
|
|
103
|
+
if rng is None:
|
|
104
|
+
rng = RNG
|
|
105
|
+
c = exp(-dt / tau)
|
|
106
|
+
|
|
107
|
+
def resample_kin(cur_kinetic_energy):
|
|
108
|
+
"""Canonical stocastical velocity rescaling.
|
|
109
|
+
|
|
110
|
+
See dx.doi.org/10.1016/j.cpc.2008.01.006
|
|
111
|
+
"""
|
|
112
|
+
R = rng.normal()
|
|
113
|
+
S = np.sum(rng.normal(size=dof-1)**2)
|
|
114
|
+
quot = (1 - c) * sigma / (dof * cur_kinetic_energy)
|
|
115
|
+
alpha = sqrt(c + quot * (S + R**2) + 2 * R * sqrt(c*quot))
|
|
116
|
+
sign = np.sign(R + sqrt(c / quot))
|
|
117
|
+
return sign * alpha
|
|
118
|
+
return resample_kin
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def berendsen_closure(sigma, dof, dt, tau=100, rng=None):
|
|
122
|
+
""" https://doi.org/10.1063/1.448118"""
|
|
123
|
+
tau_t = dt / tau
|
|
124
|
+
|
|
125
|
+
def resample_kin(cur_kinetic_energy):
|
|
126
|
+
alpha = sqrt(1 + tau_t * (sigma / cur_kinetic_energy - 1))
|
|
127
|
+
return alpha
|
|
128
|
+
return resample_kin
|