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,25 @@
|
|
|
1
|
+
from pysisyphus.cos.AdaptiveNEB import AdaptiveNEB
|
|
2
|
+
|
|
3
|
+
# [1] https://www.pnas.org/content/pnas/104/9/3031.full.pdf
|
|
4
|
+
# Zhu, 2006
|
|
5
|
+
# Original method
|
|
6
|
+
# [2] http://dx.doi.org/10.1063/1.4962019
|
|
7
|
+
# Zhang, 2016
|
|
8
|
+
# FreeEnd Adaptive NEB
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FreeEndNEB(AdaptiveNEB):
|
|
12
|
+
def __init__(self, *args, fix_first=False, fix_last=False, **kwargs):
|
|
13
|
+
"""Simple Free-End-NEB method.
|
|
14
|
+
|
|
15
|
+
Derived from AdaptiveNEB with disabled adaptation.
|
|
16
|
+
Only implements Eq. (7) from [2]. For other implementations
|
|
17
|
+
please see the commit 01bc8812ca6f1cd3645d43e0337d9e3c5fb0ba55.
|
|
18
|
+
There the other variants are present but I think Eq. (7) in [2] is
|
|
19
|
+
the simplest & best bet.
|
|
20
|
+
"""
|
|
21
|
+
kwargs["adapt"] = False
|
|
22
|
+
super().__init__(*args, fix_first=fix_first, fix_last=fix_last, **kwargs)
|
|
23
|
+
|
|
24
|
+
assert (not self.fix_first) or (not self.fix_last), \
|
|
25
|
+
"FreeEndNEB without moving end-image(s) is useless!"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pysisyphus.Geometry import Geometry
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FreezingString:
|
|
7
|
+
|
|
8
|
+
def __init__(self, images, calc_getter, max_nodes=10, opt_steps=3):
|
|
9
|
+
self.images = images
|
|
10
|
+
self.calc_getter = calc_getter
|
|
11
|
+
self.opt_steps = opt_steps
|
|
12
|
+
self.max_nodes = int(max_nodes)
|
|
13
|
+
assert self.max_nodes % 2 == 0, "max_nodes must be a multiple of 2!"
|
|
14
|
+
|
|
15
|
+
left_frontier, right_frontier = self.images
|
|
16
|
+
self.left_string = [left_frontier, ]
|
|
17
|
+
self.right_string = [right_frontier, ]
|
|
18
|
+
self.opt_steps_left = self.opt_steps
|
|
19
|
+
|
|
20
|
+
self._forces = None
|
|
21
|
+
self.atoms = left_frontier.atoms
|
|
22
|
+
coord_diff = np.linalg.norm(right_frontier.coords - left_frontier.coords)
|
|
23
|
+
self.step_length = coord_diff / (self.max_nodes+1)
|
|
24
|
+
self.set_new_frontier_nodes()
|
|
25
|
+
self.coord_type = "cart"
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def left_frontier(self):
|
|
29
|
+
return self.left_string[-1]
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def right_frontier(self):
|
|
33
|
+
return self.right_string[0]
|
|
34
|
+
|
|
35
|
+
def get_tangent(self):
|
|
36
|
+
tangent = self.right_frontier.coords - self.left_frontier.coords
|
|
37
|
+
tangent /= np.linalg.norm(tangent)
|
|
38
|
+
return tangent
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def forces(self):
|
|
42
|
+
left_forces = self.left_frontier.forces
|
|
43
|
+
right_forces = self.right_frontier.forces
|
|
44
|
+
forces = (left_forces, right_forces)
|
|
45
|
+
tangent = self.get_tangent()
|
|
46
|
+
perp_forces = np.array([f - f.dot(tangent)*tangent for f in forces]).flatten()
|
|
47
|
+
self._forces = perp_forces
|
|
48
|
+
self.energies = [self.left_frontier.energy, self.right_frontier.energy]
|
|
49
|
+
|
|
50
|
+
return self._forces
|
|
51
|
+
|
|
52
|
+
def as_xyz(self):
|
|
53
|
+
return ""
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def fully_grown(self):
|
|
57
|
+
return (len(self.left_string) + len(self.right_string)
|
|
58
|
+
== self.max_nodes + 2)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def energy(self):
|
|
62
|
+
return 10
|
|
63
|
+
# return self.energies
|
|
64
|
+
|
|
65
|
+
def set_new_frontier_nodes(self):
|
|
66
|
+
tangent = self.get_tangent()
|
|
67
|
+
new_left_coords = self.left_frontier.coords + tangent*self.step_length
|
|
68
|
+
new_left_frontier = Geometry(self.atoms, new_left_coords)
|
|
69
|
+
new_left_frontier.set_calculator(self.calc_getter())
|
|
70
|
+
self.left_string.append(new_left_frontier)
|
|
71
|
+
|
|
72
|
+
new_right_coords = self.right_frontier.coords - tangent*self.step_length
|
|
73
|
+
new_right_frontier = Geometry(self.atoms, new_right_coords)
|
|
74
|
+
new_right_frontier.set_calculator(self.calc_getter())
|
|
75
|
+
self.right_string.insert(0, new_right_frontier)
|
|
76
|
+
|
|
77
|
+
def get_new_image(self, coords, index):
|
|
78
|
+
new_image = Geometry(self.left_frontier.atoms, coords)
|
|
79
|
+
new_image.set_calculator(self.calc_getter())
|
|
80
|
+
self.images.insert(index, new_image)
|
|
81
|
+
self.log(f"Create new image; insert it before index {index}.")
|
|
82
|
+
return new_image
|
|
83
|
+
|
|
84
|
+
def reparametrize(self):
|
|
85
|
+
self.opt_steps_left -= 1
|
|
86
|
+
|
|
87
|
+
if not self.fully_grown and self.opt_steps_left == 0:
|
|
88
|
+
self.set_new_frontier_nodes()
|
|
89
|
+
self.opt_steps_left = self.opt_steps
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def allcoords(self):
|
|
93
|
+
return np.array([img.coords for img in self.left_string + self.right_string]).flatten()
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def coords(self):
|
|
97
|
+
return np.array((self.left_frontier.coords, self.right_frontier.coords)).flatten()
|
|
98
|
+
|
|
99
|
+
@coords.setter
|
|
100
|
+
def coords(self, coords):
|
|
101
|
+
left, right = coords.reshape(2, -1)
|
|
102
|
+
self.left_frontier.coords = left
|
|
103
|
+
self.right_frontier.coords = right
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pysisyphus.cos.ChainOfStates import ChainOfStates
|
|
4
|
+
from pysisyphus.Geometry import Geometry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GrowingChainOfStates(ChainOfStates):
|
|
8
|
+
|
|
9
|
+
def __init__(self, images, calc_getter, max_nodes=10,
|
|
10
|
+
**kwargs):
|
|
11
|
+
super().__init__(images, **kwargs)
|
|
12
|
+
|
|
13
|
+
self.max_nodes = max_nodes
|
|
14
|
+
self.calc_getter = calc_getter
|
|
15
|
+
self.zero_step = np.zeros_like(self.images[0].coords)
|
|
16
|
+
|
|
17
|
+
def get_new_image_from_coords(self, coords, index):
|
|
18
|
+
new_image = Geometry(
|
|
19
|
+
self.image_atoms,
|
|
20
|
+
coords,
|
|
21
|
+
coord_type=self.coord_type,
|
|
22
|
+
coord_kwargs={"typed_prims": self.typed_prims},
|
|
23
|
+
freeze_atoms=self.images[0].freeze_atoms.copy()
|
|
24
|
+
)
|
|
25
|
+
new_image.set_calculator(self.calc_getter())
|
|
26
|
+
self.images.insert(index, new_image)
|
|
27
|
+
self.log(f"Create new image; insert it before index {index}.")
|
|
28
|
+
return new_image
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def arc_dims(self):
|
|
32
|
+
cds = [0, ]
|
|
33
|
+
for i, image in enumerate(self.images[:-1]):
|
|
34
|
+
next_image = self.images[i+1]
|
|
35
|
+
diff = np.linalg.norm(next_image - image)
|
|
36
|
+
cds.append(diff)
|
|
37
|
+
cds = np.cumsum(cds)
|
|
38
|
+
tot_length = cds[-1]
|
|
39
|
+
norm_cds = cds / cds.max()
|
|
40
|
+
return tot_length, norm_cds
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def max_image_num(self):
|
|
44
|
+
return self.max_nodes + 2
|
|
45
|
+
|
|
46
|
+
def new_node_coords(self, k):
|
|
47
|
+
l = (self.max_nodes-k) / (self.max_nodes+1-k)
|
|
48
|
+
kth_coords = self.images[k].coords
|
|
49
|
+
last_coords = self.images[-1].coords
|
|
50
|
+
new_coords = l*kth_coords + (1-l)*last_coords
|
|
51
|
+
return new_coords
|
|
52
|
+
|
|
53
|
+
def set_new_node(self, k):
|
|
54
|
+
new_coords = self.new_node_coords(k)
|
|
55
|
+
new_node = Geometry(
|
|
56
|
+
self.image_atoms,
|
|
57
|
+
new_coords,
|
|
58
|
+
freeze_atoms=self.images[0].freeze_atoms.copy()
|
|
59
|
+
)
|
|
60
|
+
new_node.set_calculator(self.calc_getter())
|
|
61
|
+
self.images.insert(k+1, new_node)
|
|
62
|
+
return new_node
|
|
63
|
+
|
|
64
|
+
def prepare_opt_cycle(self, *args, **kwargs):
|
|
65
|
+
parent_result = super().prepare_opt_cycle(*args, **kwargs)
|
|
66
|
+
|
|
67
|
+
# Compare size of coords arrays to determine if new nodes
|
|
68
|
+
# were added in the last reparametrization.
|
|
69
|
+
last_size = self.coords_list[-1].size
|
|
70
|
+
length_changed = last_size != self.coords.size
|
|
71
|
+
return parent_result or length_changed
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# [1] https://aip.scitation.org/doi/abs/10.1063/1.1885467
|
|
2
|
+
# Quapp, 2005
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from pysisyphus.helpers_pure import rms
|
|
11
|
+
from pysisyphus.intcoords.helpers import get_weighted_bond_mode
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GrowingNT:
|
|
15
|
+
logger = logging.getLogger("cos")
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
geom,
|
|
20
|
+
step_len=0.5,
|
|
21
|
+
rms_thresh=1.7e-3,
|
|
22
|
+
r=None,
|
|
23
|
+
final_geom=None,
|
|
24
|
+
between=None,
|
|
25
|
+
bonds=None,
|
|
26
|
+
r_update=True,
|
|
27
|
+
r_update_thresh=1.0,
|
|
28
|
+
stop_after_ts=False,
|
|
29
|
+
require_imag_freq=0.0,
|
|
30
|
+
hessian_at_ts=False,
|
|
31
|
+
out_dir=".",
|
|
32
|
+
dump=True,
|
|
33
|
+
):
|
|
34
|
+
assert geom.coord_type == "cart"
|
|
35
|
+
|
|
36
|
+
self.geom = geom
|
|
37
|
+
self.step_len = step_len
|
|
38
|
+
self.rms_thresh = rms_thresh
|
|
39
|
+
self.final_geom = final_geom
|
|
40
|
+
self.between = between
|
|
41
|
+
self.bonds = bonds
|
|
42
|
+
self.r_update = r_update
|
|
43
|
+
self.r_update_thresh = r_update_thresh
|
|
44
|
+
self.stop_after_ts = stop_after_ts
|
|
45
|
+
self.require_imag_freq = require_imag_freq
|
|
46
|
+
self.hessian_at_ts = hessian_at_ts
|
|
47
|
+
self.out_dir = Path(out_dir)
|
|
48
|
+
self.dump = dump
|
|
49
|
+
|
|
50
|
+
if not self.out_dir.exists():
|
|
51
|
+
os.mkdir(self.out_dir)
|
|
52
|
+
|
|
53
|
+
self.coord_type = self.geom.coord_type
|
|
54
|
+
if self.final_geom:
|
|
55
|
+
self.converge_to_geom = self.final_geom
|
|
56
|
+
|
|
57
|
+
# Determine search direction
|
|
58
|
+
self.r = self.get_r(self.geom, self.final_geom, self.bonds, r)
|
|
59
|
+
self.r_org = self.r.copy()
|
|
60
|
+
# Determine appropriate step_len from between
|
|
61
|
+
if final_geom and self.between:
|
|
62
|
+
self.step_len = np.linalg.norm(final_geom.coords - geom.coords) / (
|
|
63
|
+
self.between + 1
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
self._initialized = False
|
|
67
|
+
self.images = [self.geom.copy()]
|
|
68
|
+
self.all_energies = list()
|
|
69
|
+
self.all_real_forces = list()
|
|
70
|
+
self.sp_images = [self.geom.copy()] # Stationary points
|
|
71
|
+
self.ts_images = list()
|
|
72
|
+
self.min_images = list()
|
|
73
|
+
|
|
74
|
+
self.ts_imag_freqs = list()
|
|
75
|
+
|
|
76
|
+
# Right now this leads to a gradient calculation in the momement,
|
|
77
|
+
# this object is constructed, which is bad.
|
|
78
|
+
self.initialize()
|
|
79
|
+
|
|
80
|
+
if self.dump:
|
|
81
|
+
self.trj_fn = self.get_path("newton_trajectory_trj.xyz")
|
|
82
|
+
|
|
83
|
+
def get_path(self, fn):
|
|
84
|
+
return self.out_dir / fn
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def get_r(geom, final_geom, bonds, r):
|
|
88
|
+
if final_geom:
|
|
89
|
+
r = final_geom - geom
|
|
90
|
+
# self.converge_to_geom = self.final_geom
|
|
91
|
+
elif bonds is not None:
|
|
92
|
+
r = get_weighted_bond_mode(bonds, geom.coords3d)
|
|
93
|
+
# Use 'r' as it is
|
|
94
|
+
elif r is not None:
|
|
95
|
+
pass
|
|
96
|
+
else:
|
|
97
|
+
raise Exception("Please supply either 'r' or 'final_geom'!")
|
|
98
|
+
|
|
99
|
+
r = r / np.linalg.norm(r)
|
|
100
|
+
return r
|
|
101
|
+
|
|
102
|
+
def log(self, message):
|
|
103
|
+
self.logger.debug(message)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def r(self):
|
|
107
|
+
"""Parallel/search direction."""
|
|
108
|
+
return self._r
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def P(self):
|
|
112
|
+
"""Projector that keeps perpendicular component."""
|
|
113
|
+
return self._P
|
|
114
|
+
|
|
115
|
+
@r.setter
|
|
116
|
+
def r(self, r):
|
|
117
|
+
"""Update r and calculate new projector."""
|
|
118
|
+
self._r = r
|
|
119
|
+
self._P = np.eye(self.coords.size) - np.outer(self.r, self.r)
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def atoms(self):
|
|
123
|
+
return self.geom.atoms
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def coords(self):
|
|
127
|
+
return self.geom.coords
|
|
128
|
+
|
|
129
|
+
@coords.setter
|
|
130
|
+
def coords(self, coords):
|
|
131
|
+
self.geom.coords = coords
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def cart_coords(self):
|
|
135
|
+
return self.geom.cart_coords
|
|
136
|
+
|
|
137
|
+
def grow_image(self):
|
|
138
|
+
self.images[-1] = self.geom.copy()
|
|
139
|
+
# Update coordinates of newly grown image. Try to use Eq. (6) in [1].
|
|
140
|
+
if self._initialized and self.final_geom and self.between:
|
|
141
|
+
m = self.between + 2
|
|
142
|
+
k = len(self.images) - 1
|
|
143
|
+
lambda_ = (m - k) / (m + 1 - k)
|
|
144
|
+
# Adapted from [6] to produce a step instead of new coords
|
|
145
|
+
step = self.coords * (lambda_ - 1) + (1 - lambda_) * self.final_geom.coords
|
|
146
|
+
# If no final image is given we just displace along r
|
|
147
|
+
else:
|
|
148
|
+
step = self.step_len * self.r
|
|
149
|
+
self.coords = self.coords + step
|
|
150
|
+
|
|
151
|
+
# Calculate energy and forces at newly grown geometry and append new frontier
|
|
152
|
+
# image.
|
|
153
|
+
real_forces = self.geom.forces
|
|
154
|
+
energy = self.geom.energy
|
|
155
|
+
self.all_energies.append(energy)
|
|
156
|
+
self.all_real_forces.append(real_forces)
|
|
157
|
+
self.images.append(self.geom)
|
|
158
|
+
|
|
159
|
+
def initialize(self):
|
|
160
|
+
assert not self._initialized, "GrowingNT.initialize() can only be called once!"
|
|
161
|
+
# Calculation at initial geometry
|
|
162
|
+
init_results = self.geom.get_energy_and_forces_at(self.images[0].coords)
|
|
163
|
+
self.all_energies.append(init_results["energy"])
|
|
164
|
+
self.all_real_forces.append(init_results["forces"])
|
|
165
|
+
# Do initial displacement
|
|
166
|
+
self.grow_image()
|
|
167
|
+
# Indicate the GrowingNT was properly initialized
|
|
168
|
+
self._initialized = True
|
|
169
|
+
|
|
170
|
+
def calc_hessian_for(self, other_geom):
|
|
171
|
+
res = self.geom.get_energy_and_cart_hessian_at(other_geom.cart_coords)
|
|
172
|
+
cart_hessian = res["hessian"]
|
|
173
|
+
return cart_hessian
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def energy(self):
|
|
177
|
+
return self.geom.energy
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def forces(self):
|
|
181
|
+
forces = self.geom.forces
|
|
182
|
+
perp_forces = self.P.dot(forces)
|
|
183
|
+
return perp_forces
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def cart_forces(self):
|
|
187
|
+
return self.geom.cart_forces
|
|
188
|
+
|
|
189
|
+
def get_energy_at(self, coords):
|
|
190
|
+
return self.geom.get_energy_at(coords)
|
|
191
|
+
|
|
192
|
+
def get_energy_and_forces_at(self, coords):
|
|
193
|
+
return self.geom.get_energy_and_forces_at(coords)
|
|
194
|
+
|
|
195
|
+
def as_xyz(self):
|
|
196
|
+
return self.geom.as_xyz()
|
|
197
|
+
|
|
198
|
+
def clear_passed(self):
|
|
199
|
+
self.passed_min = False
|
|
200
|
+
self.passed_ts = False
|
|
201
|
+
|
|
202
|
+
def reparametrize(self):
|
|
203
|
+
"""Check if GNT can be grown."""
|
|
204
|
+
|
|
205
|
+
# Real, unprojected, forces of the underlying geometry
|
|
206
|
+
real_forces = self.geom.forces
|
|
207
|
+
energy = self.energy
|
|
208
|
+
# Update the last element in all_real_forces and energies with the
|
|
209
|
+
# current values.
|
|
210
|
+
self.all_real_forces[-1] = real_forces
|
|
211
|
+
self.all_energies[-1] = energy
|
|
212
|
+
|
|
213
|
+
# See if we can grow the NT, by checking the convergence of the frontier
|
|
214
|
+
# image using projected forces.
|
|
215
|
+
forces = self.forces
|
|
216
|
+
can_grow = rms(forces) <= self.rms_thresh
|
|
217
|
+
|
|
218
|
+
if can_grow:
|
|
219
|
+
if self.dump:
|
|
220
|
+
with open(self.trj_fn, "w") as handle:
|
|
221
|
+
handle.write("\n".join([geom.as_xyz() for geom in self.images]))
|
|
222
|
+
|
|
223
|
+
r"""
|
|
224
|
+
Check if we passed a stationary point (SP).
|
|
225
|
+
^ Energy
|
|
226
|
+
|
|
|
227
|
+
| -3 -1 -2
|
|
228
|
+
| \ / / \
|
|
229
|
+
| \ / / \
|
|
230
|
+
| -2 -3 -1
|
|
231
|
+
| Minimum TS
|
|
232
|
+
"""
|
|
233
|
+
ae = self.all_energies # Shortcut
|
|
234
|
+
self.passed_min = len(ae) >= 3 and ae[-3] > ae[-2] < ae[-1]
|
|
235
|
+
self.passed_ts = len(ae) >= 3 and ae[-3] < ae[-2] > ae[-1]
|
|
236
|
+
passed_sp = self.passed_min or self.passed_ts
|
|
237
|
+
if passed_sp:
|
|
238
|
+
sp_image = self.images[-2].copy()
|
|
239
|
+
sp_kind = "Minimum" if self.passed_min else "TS"
|
|
240
|
+
self.sp_images.append(sp_image)
|
|
241
|
+
self.log(
|
|
242
|
+
f"Passed stationary point! It seems to be a {sp_kind}."
|
|
243
|
+
f"\n{sp_image.as_xyz()}"
|
|
244
|
+
)
|
|
245
|
+
if self.passed_ts:
|
|
246
|
+
self.ts_images.append(sp_image)
|
|
247
|
+
if self.hessian_at_ts:
|
|
248
|
+
sp_hessian = self.calc_hessian_for(sp_image)
|
|
249
|
+
nus, *_ = sp_image.get_normal_modes(sp_hessian)
|
|
250
|
+
self.log(f"First 5 frequencies: {nus[:5]}")
|
|
251
|
+
if self.require_imag_freq < 0.0:
|
|
252
|
+
try:
|
|
253
|
+
sp_hessian
|
|
254
|
+
except NameError:
|
|
255
|
+
sp_hessian = self.calc_hessian_for(sp_image)
|
|
256
|
+
self.ts_imag_freqs.append(
|
|
257
|
+
sp_image.get_imag_frequencies(sp_hessian)
|
|
258
|
+
)
|
|
259
|
+
elif self.passed_min:
|
|
260
|
+
self.min_images.append(sp_image)
|
|
261
|
+
|
|
262
|
+
# Update direction 'r', if requested
|
|
263
|
+
r_new = self.get_r(self.geom, self.final_geom, self.bonds, self.r)
|
|
264
|
+
r_dot = r_new.dot(self.r)
|
|
265
|
+
r_org_dot = r_new.dot(self.r_org)
|
|
266
|
+
self.log(f"r.dot(r')={r_dot:.6f} r_org.dot(r')={r_org_dot:.6f}")
|
|
267
|
+
if (
|
|
268
|
+
self.r_update
|
|
269
|
+
and (r_org_dot <= self.r_update_thresh)
|
|
270
|
+
and self.passed_min
|
|
271
|
+
):
|
|
272
|
+
self.r = r_new
|
|
273
|
+
self.log("Updated r")
|
|
274
|
+
|
|
275
|
+
# Grow new image
|
|
276
|
+
self.grow_image()
|
|
277
|
+
assert (
|
|
278
|
+
len(self.images) == len(self.all_energies) == len(self.all_real_forces)
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
self.did_reparametrization = can_grow
|
|
282
|
+
return can_grow
|
|
283
|
+
|
|
284
|
+
def check_convergence(self, *args, **kwargs):
|
|
285
|
+
if len(self.ts_images) == 0:
|
|
286
|
+
return False
|
|
287
|
+
|
|
288
|
+
converged = self.stop_after_ts
|
|
289
|
+
if self.require_imag_freq:
|
|
290
|
+
converged = (
|
|
291
|
+
converged and self.ts_imag_freqs[-1][0] <= self.require_imag_freq
|
|
292
|
+
)
|
|
293
|
+
return converged
|
|
294
|
+
|
|
295
|
+
def get_additional_print(self):
|
|
296
|
+
if self.did_reparametrization:
|
|
297
|
+
img_num = len(self.images)
|
|
298
|
+
str_ = f"Grew Newton trajectory to {img_num} images."
|
|
299
|
+
if self.passed_min:
|
|
300
|
+
str_ += f" Passed minimum geometry at image {img_num-1}."
|
|
301
|
+
elif self.passed_ts:
|
|
302
|
+
str_ += f" Passed transition state geometry at image {img_num-1}."
|
|
303
|
+
else:
|
|
304
|
+
str_ = None
|
|
305
|
+
|
|
306
|
+
self.did_reparametrization = False
|
|
307
|
+
self.clear_passed()
|
|
308
|
+
|
|
309
|
+
return str_
|