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
pysisyphus/plot.py
ADDED
|
@@ -0,0 +1,1031 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import sys
|
|
4
|
+
import textwrap
|
|
5
|
+
|
|
6
|
+
import h5py
|
|
7
|
+
import matplotlib
|
|
8
|
+
from matplotlib.animation import FuncAnimation
|
|
9
|
+
from matplotlib.patches import Rectangle
|
|
10
|
+
import matplotlib.image as mpimg
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
import numpy as np
|
|
13
|
+
from scipy.interpolate import splrep, splev
|
|
14
|
+
|
|
15
|
+
from pysisyphus.constants import AU2KJPERMOL, AU2KCALPERMOL, AU2EV
|
|
16
|
+
from pysisyphus.config import OUT_DIR_DEFAULT
|
|
17
|
+
from pysisyphus.dynamics import Gaussian
|
|
18
|
+
from pysisyphus.io import parse_xyz
|
|
19
|
+
from pysisyphus.peakdetect import peakdetect
|
|
20
|
+
from pysisyphus.wrapper.jmol import render_cdd_cube
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
CDD_PNG_FNS = "cdd_png_fns"
|
|
24
|
+
"""
|
|
25
|
+
Default to kJ mol⁻¹. DE_LABEL and get_en_conv will be overwritten when
|
|
26
|
+
run() is executed. Both are still defined here, to make the functions
|
|
27
|
+
from plot.py importable.
|
|
28
|
+
"""
|
|
29
|
+
DE_LABEL = r"$\Delta$E / kJ mol⁻¹"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_en_conv():
|
|
33
|
+
return AU2KJPERMOL, "kJ mol⁻¹"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_force_unit(coord_type):
|
|
37
|
+
force_unit = "$E_h$ Bohr⁻¹"
|
|
38
|
+
if coord_type != "cart":
|
|
39
|
+
force_unit += " (rad)⁻¹"
|
|
40
|
+
return force_unit
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def spline_plot_cycles(cart_coords, energies):
|
|
44
|
+
num_cycles = energies.shape[1]
|
|
45
|
+
|
|
46
|
+
fig, ax = plt.subplots()
|
|
47
|
+
colors = matplotlib.cm.Greys(np.linspace(0.2, 1, num=num_cycles))
|
|
48
|
+
# for cycle, color in zip(energies, colors):
|
|
49
|
+
for i, (cycle, color) in enumerate(zip(energies, colors)):
|
|
50
|
+
ax.plot(cycle, "o-", color=color)
|
|
51
|
+
ax.set_title("COS image energies")
|
|
52
|
+
|
|
53
|
+
kwargs = {
|
|
54
|
+
"ls": ":",
|
|
55
|
+
"color": "darkgrey",
|
|
56
|
+
}
|
|
57
|
+
# Try to spline the last cycle to get an estimate for the spliend HEI
|
|
58
|
+
try:
|
|
59
|
+
last_cycle = energies[-1]
|
|
60
|
+
num_images = last_cycle.size
|
|
61
|
+
spl = splrep(np.arange(num_images), last_cycle)
|
|
62
|
+
# Calculate interpolated values
|
|
63
|
+
x2 = np.linspace(0, num_images, 100)
|
|
64
|
+
y2 = splev(x2, spl)
|
|
65
|
+
# Only consider maxima
|
|
66
|
+
peak_inds, _ = peakdetect(y2, lookahead=2)
|
|
67
|
+
if not peak_inds:
|
|
68
|
+
ax.plot(x2, y2)
|
|
69
|
+
else:
|
|
70
|
+
peak_inds = np.array(peak_inds)[:, 0].astype(int)
|
|
71
|
+
peak_xs = x2[peak_inds]
|
|
72
|
+
peak_ys = y2[peak_inds]
|
|
73
|
+
ax.plot(x2, y2, peak_xs, peak_ys, "x")
|
|
74
|
+
for px, py in zip(peak_xs, peak_ys):
|
|
75
|
+
ax.axhline(y=py, **kwargs)
|
|
76
|
+
line = matplotlib.lines.Line2D([px, px], [0, py], **kwargs)
|
|
77
|
+
ax.add_line(line)
|
|
78
|
+
except TypeError:
|
|
79
|
+
print("Not enough images for splining!")
|
|
80
|
+
|
|
81
|
+
# Always draw a line at the minimum y=0
|
|
82
|
+
ax.axhline(y=0, **kwargs)
|
|
83
|
+
ax.set_xlabel("Image")
|
|
84
|
+
_, en_unit = get_en_conv()
|
|
85
|
+
ax.set_ylabel(DE_LABEL)
|
|
86
|
+
|
|
87
|
+
return fig, ax
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def plot_cycle(cart_coords, energies):
|
|
91
|
+
# Plot last_cycle
|
|
92
|
+
fig, ax = plt.subplots()
|
|
93
|
+
last_energies = energies[-1].copy()
|
|
94
|
+
xs = np.arange(len(last_energies))
|
|
95
|
+
ax.plot(xs, last_energies, "o-")
|
|
96
|
+
ax.set_xlabel("Image")
|
|
97
|
+
ax.set_ylabel(DE_LABEL)
|
|
98
|
+
ax.set_title(f"COS image energies, (last) cycle {len(energies)-1}")
|
|
99
|
+
|
|
100
|
+
first_image_en = last_energies[0]
|
|
101
|
+
last_image_en = last_energies[-1]
|
|
102
|
+
max_en_ind = np.nanargmax(last_energies)
|
|
103
|
+
max_en = last_energies[max_en_ind]
|
|
104
|
+
nans = np.isnan(last_energies).sum()
|
|
105
|
+
if nans:
|
|
106
|
+
print(
|
|
107
|
+
f"The COS seems to be not fully grown (yet), {nans} energies are missing."
|
|
108
|
+
)
|
|
109
|
+
print(
|
|
110
|
+
"Barrier heights using actual energies (not splined) from "
|
|
111
|
+
f"cycle {energies.shape[0]-1}."
|
|
112
|
+
)
|
|
113
|
+
print(f"\tHighest energy image (HEI) at index {max_en_ind} (0-based)")
|
|
114
|
+
|
|
115
|
+
first_barr = max_en - first_image_en
|
|
116
|
+
_, en_unit = get_en_conv()
|
|
117
|
+
print(f"\tBarrier between first image and HEI: {first_barr:.1f} {en_unit}")
|
|
118
|
+
last_barr = max_en - last_image_en
|
|
119
|
+
print(f"\tBarrier between last image and HEI: {last_barr:.1f} {en_unit}")
|
|
120
|
+
|
|
121
|
+
return fig, ax
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def anim_cos(cart_coords, energies):
|
|
125
|
+
num_cycles = cart_coords.shape[0]
|
|
126
|
+
|
|
127
|
+
# Also do an animation
|
|
128
|
+
min_ = np.nanmin(energies)
|
|
129
|
+
max_ = np.nanmax(energies)
|
|
130
|
+
|
|
131
|
+
coord_diffs = np.linalg.norm(cart_coords - cart_coords[0][0], axis=2)
|
|
132
|
+
fig, ax = plt.subplots()
|
|
133
|
+
|
|
134
|
+
# Initial energies
|
|
135
|
+
lines = ax.plot(coord_diffs[0], energies[0], "o-")
|
|
136
|
+
y_max = max_ - min_
|
|
137
|
+
ax.set_ylim(0, y_max)
|
|
138
|
+
ax.set_xlabel("Coordinate differences / Bohr")
|
|
139
|
+
ax.set_ylabel(DE_LABEL)
|
|
140
|
+
|
|
141
|
+
def update_func(i):
|
|
142
|
+
fig.suptitle("Cycle {}".format(i))
|
|
143
|
+
lines[0].set_xdata(coord_diffs[i])
|
|
144
|
+
lines[0].set_ydata(energies[i])
|
|
145
|
+
|
|
146
|
+
def animate():
|
|
147
|
+
animation = FuncAnimation(
|
|
148
|
+
fig,
|
|
149
|
+
update_func,
|
|
150
|
+
frames=num_cycles,
|
|
151
|
+
interval=250,
|
|
152
|
+
)
|
|
153
|
+
return animation
|
|
154
|
+
|
|
155
|
+
anim = animate()
|
|
156
|
+
return anim, fig, ax
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def load_h5(h5_fn, h5_group, datasets=None, attrs=None):
|
|
160
|
+
if datasets is None:
|
|
161
|
+
datasets = list()
|
|
162
|
+
|
|
163
|
+
if attrs is None:
|
|
164
|
+
attrs = list()
|
|
165
|
+
|
|
166
|
+
with h5py.File(h5_fn, "r") as handle:
|
|
167
|
+
group = handle[h5_group]
|
|
168
|
+
|
|
169
|
+
atoms = group.attrs["atoms"]
|
|
170
|
+
cur_cycle = group.attrs["cur_cycle"]
|
|
171
|
+
coord_size = group.attrs["coord_size"]
|
|
172
|
+
num_cycles = cur_cycle + 1
|
|
173
|
+
|
|
174
|
+
image_nums = group["image_nums"][:num_cycles].astype(int)
|
|
175
|
+
image_inds = group["image_inds"][:num_cycles].astype(int)
|
|
176
|
+
|
|
177
|
+
_datasets = dict()
|
|
178
|
+
for ds in datasets:
|
|
179
|
+
try:
|
|
180
|
+
_datasets[ds] = group[ds][:num_cycles]
|
|
181
|
+
except KeyError:
|
|
182
|
+
print(f"Could not load dataset '{ds}' from HDF5 file.")
|
|
183
|
+
|
|
184
|
+
_attrs = dict()
|
|
185
|
+
for a in attrs:
|
|
186
|
+
try:
|
|
187
|
+
_attrs[a] = group.attrs[a]
|
|
188
|
+
except KeyError:
|
|
189
|
+
print(f"Could not load attribute '{a}' from HDF5 file.")
|
|
190
|
+
|
|
191
|
+
en_conv, _ = get_en_conv()
|
|
192
|
+
if "energies" in _datasets:
|
|
193
|
+
ens = _datasets["energies"]
|
|
194
|
+
ens -= ens.min()
|
|
195
|
+
ens *= en_conv
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
# We can't use coord_size because coord_type may be != cart and then
|
|
199
|
+
# coord_size gives the number of internals.
|
|
200
|
+
cart_shape = (num_cycles, -1, 3 * len(atoms))
|
|
201
|
+
_datasets["cart_coords"] = _datasets["cart_coords"].reshape(cart_shape)
|
|
202
|
+
except KeyError:
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
# Here we can use coord_size because forces will always be in the same
|
|
207
|
+
# coordinate system as the actual coordinates.
|
|
208
|
+
_datasets["forces"] = _datasets["forces"].reshape((num_cycles, -1, coord_size))
|
|
209
|
+
except KeyError:
|
|
210
|
+
pass
|
|
211
|
+
|
|
212
|
+
def sort_by_image(arr):
|
|
213
|
+
by_image = np.full_like(arr, np.nan)
|
|
214
|
+
for cyc, (img_ind, img_num) in enumerate(zip(image_inds, image_nums)):
|
|
215
|
+
img_ind = img_ind[:img_num]
|
|
216
|
+
by_image[cyc, img_ind] = arr[cyc, :img_num]
|
|
217
|
+
return by_image
|
|
218
|
+
|
|
219
|
+
for k, v in _datasets.items():
|
|
220
|
+
_datasets[k] = sort_by_image(v)
|
|
221
|
+
|
|
222
|
+
# Also copy requested attributes into dictionary
|
|
223
|
+
_datasets.update(_attrs)
|
|
224
|
+
|
|
225
|
+
return _datasets
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def plot_cos_energies(h5_fn="optimization.h5", h5_group="opt"):
|
|
229
|
+
results = load_h5(
|
|
230
|
+
h5_fn, h5_group, datasets=("cart_coords", "energies"), attrs=("is_cos",)
|
|
231
|
+
)
|
|
232
|
+
cart_coords = results["cart_coords"]
|
|
233
|
+
energies = results["energies"]
|
|
234
|
+
|
|
235
|
+
assert results["is_cos"]
|
|
236
|
+
|
|
237
|
+
# Splined last cycle and plot of all cycles
|
|
238
|
+
fig_, ax_ = spline_plot_cycles(
|
|
239
|
+
cart_coords, energies
|
|
240
|
+
) # lgtm [py/unused-local-variable]
|
|
241
|
+
# Plot last cycle
|
|
242
|
+
fig_last, ax_last = plot_cycle(
|
|
243
|
+
cart_coords, energies
|
|
244
|
+
) # lgtm [py/unused-local-variable]
|
|
245
|
+
# Plot animation
|
|
246
|
+
anim, fig_anim, ax_anim = anim_cos(
|
|
247
|
+
cart_coords, energies
|
|
248
|
+
) # lgtm [py/unused-local-variable]
|
|
249
|
+
|
|
250
|
+
plt.show()
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def plot_cos_forces(h5_fn="optimization.h5", h5_group="opt", last=15):
|
|
254
|
+
results = load_h5(
|
|
255
|
+
h5_fn,
|
|
256
|
+
h5_group,
|
|
257
|
+
datasets=("energies", "forces",),
|
|
258
|
+
attrs=("is_cos", "coord_type", "max_force_thresh", "rms_force_thresh"),
|
|
259
|
+
)
|
|
260
|
+
cycles = len(results["energies"])
|
|
261
|
+
last_cycles = np.arange(cycles)[-last:]
|
|
262
|
+
energies = results["energies"][-last:]
|
|
263
|
+
forces = results["forces"][-last:]
|
|
264
|
+
coord_type = results["coord_type"]
|
|
265
|
+
|
|
266
|
+
assert results["is_cos"]
|
|
267
|
+
|
|
268
|
+
last_axis = forces.ndim - 1
|
|
269
|
+
max_ = np.nanmax(np.abs(forces), axis=last_axis)
|
|
270
|
+
rms = np.sqrt(np.mean(forces ** 2, axis=last_axis))
|
|
271
|
+
hei_indices = energies.argmax(axis=1)
|
|
272
|
+
force_unit = get_force_unit(coord_type)
|
|
273
|
+
|
|
274
|
+
fmt = ".6f"
|
|
275
|
+
print("HEI forces in E_h / a0")
|
|
276
|
+
for i, hei_index in enumerate(hei_indices):
|
|
277
|
+
cycle = last_cycles[i]
|
|
278
|
+
hei_max = max_[i, hei_index]
|
|
279
|
+
hei_rms = rms[i, hei_index]
|
|
280
|
+
print(f"\tCycle {cycle:03d}: max(forces)={hei_max:{fmt}}, rms(forces)={hei_rms:{fmt}}")
|
|
281
|
+
|
|
282
|
+
fig, (ax0, ax1) = plt.subplots(sharex=True, nrows=2)
|
|
283
|
+
|
|
284
|
+
def plot(ax, data, title):
|
|
285
|
+
num = data.shape[0]
|
|
286
|
+
alphas = np.linspace(0.125, 1, num=num)
|
|
287
|
+
colors = matplotlib.cm.Greys(np.linspace(0, 1, num=num))
|
|
288
|
+
colors[-1] = (1., 0., 0., 1.) # use red for latest cycle
|
|
289
|
+
for row, color, alpha in zip(data, colors, alphas):
|
|
290
|
+
ax.plot(row, "o-", color=color, alpha=alpha)
|
|
291
|
+
ax.set_ylabel(force_unit)
|
|
292
|
+
ax.set_yscale("log")
|
|
293
|
+
if title:
|
|
294
|
+
ax.set_title(title)
|
|
295
|
+
|
|
296
|
+
plot(ax0, max_, "max(perp. forces)")
|
|
297
|
+
plot(ax1, rms, "rms(perp. forces)")
|
|
298
|
+
try:
|
|
299
|
+
ax0.axhline(results["max_force_thresh"], ls="--", c="k", label="Conv. thresh.")
|
|
300
|
+
ax0.legend()
|
|
301
|
+
ax1.axhline(results["rms_force_thresh"], ls="--", c="k", label="Conv. thresh.")
|
|
302
|
+
ax1.legend()
|
|
303
|
+
except KeyError:
|
|
304
|
+
print("Could not find max/rms entries for force threshold on HDF5 file!")
|
|
305
|
+
ax1.set_xlabel("Image")
|
|
306
|
+
|
|
307
|
+
plt.tight_layout()
|
|
308
|
+
plt.show()
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def plot_all_energies(h5):
|
|
312
|
+
with h5py.File(h5) as handle:
|
|
313
|
+
energies = handle["all_energies"][:]
|
|
314
|
+
roots = handle["roots"][:]
|
|
315
|
+
flips = handle["root_flips"][:]
|
|
316
|
+
ovlp_type = handle.attrs["ovlp_type"]
|
|
317
|
+
ovlp_with = handle.attrs["ovlp_with"]
|
|
318
|
+
print(f"Overlap type: '{ovlp_type}', overlaps with: '{ovlp_with}'.")
|
|
319
|
+
print(f"Found a total of {len(roots)} steps.")
|
|
320
|
+
print(f"{flips} root flips occured.")
|
|
321
|
+
|
|
322
|
+
energies -= energies.min()
|
|
323
|
+
energies *= AU2EV
|
|
324
|
+
|
|
325
|
+
# Don't plot steps where flips occured
|
|
326
|
+
# energies = np.concatenate((energies[0][None,:], energies[1:,:][~flips]), axis=0)
|
|
327
|
+
energies_ = list()
|
|
328
|
+
roots_ = list()
|
|
329
|
+
steps = list()
|
|
330
|
+
for i, root_flip in enumerate(flips[:-1]):
|
|
331
|
+
if root_flip:
|
|
332
|
+
print(f"Root flip occured between {i} and {i+1}.")
|
|
333
|
+
continue
|
|
334
|
+
print(f"Using step {i}")
|
|
335
|
+
energies_.append(energies[i])
|
|
336
|
+
roots_.append(roots[i])
|
|
337
|
+
steps.append(i)
|
|
338
|
+
# Don't append last step if a root flip occured there.
|
|
339
|
+
if not flips[-1]:
|
|
340
|
+
energies_.append(energies[-1])
|
|
341
|
+
roots_.append(roots[-1])
|
|
342
|
+
steps.append(i + 1)
|
|
343
|
+
else:
|
|
344
|
+
print("Root flip occured in the last step. Not showing the last step.")
|
|
345
|
+
|
|
346
|
+
energies = np.array(energies_)
|
|
347
|
+
roots = np.array(roots_)
|
|
348
|
+
|
|
349
|
+
fig, ax = plt.subplots()
|
|
350
|
+
for i, state in enumerate(energies.T):
|
|
351
|
+
ax.plot(steps, state, "o-", label=f"State {i:03d}")
|
|
352
|
+
ax.legend(loc="lower center", ncol=3)
|
|
353
|
+
ax.set_title(f"'{ovlp_type}' overlaps with '{ovlp_with}'")
|
|
354
|
+
ax.set_xlabel("Cycle")
|
|
355
|
+
ax.set_ylabel(r"$\Delta$E / eV")
|
|
356
|
+
root_ens = [s[r] for s, r in zip(energies, roots)]
|
|
357
|
+
ax.plot(steps, root_ens, "--k")
|
|
358
|
+
plt.tight_layout()
|
|
359
|
+
plt.show()
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def plot_md(h5_group="run"):
|
|
363
|
+
with h5py.File("md.h5", "r") as handle:
|
|
364
|
+
group = handle[h5_group]
|
|
365
|
+
|
|
366
|
+
steps = group["step"][:]
|
|
367
|
+
ens = group["energy_tot"][:]
|
|
368
|
+
ens_conserved = group["energy_conserved"][:]
|
|
369
|
+
Ts = group["T"][:]
|
|
370
|
+
T_avgs = group["T_avg"][:]
|
|
371
|
+
# coords = group["cart_coords"][:]
|
|
372
|
+
# velocities = group["velocity"][:]
|
|
373
|
+
|
|
374
|
+
dt = group.attrs["dt"]
|
|
375
|
+
T_target = group.attrs["T_target"]
|
|
376
|
+
|
|
377
|
+
_, (ax0, ax1, ax2) = plt.subplots(nrows=3, sharex=True)
|
|
378
|
+
dts = steps * dt
|
|
379
|
+
en_conv, en_unit = get_en_conv()
|
|
380
|
+
ens *= en_conv
|
|
381
|
+
mean = ens.mean()
|
|
382
|
+
ens -= mean
|
|
383
|
+
ax0.plot(dts, ens)
|
|
384
|
+
ax0.axhline(0, ls="--", c="k")
|
|
385
|
+
ax0.set_ylabel(r"$E - \overline{E}$ / " + en_unit)
|
|
386
|
+
ax0.set_title("Energy")
|
|
387
|
+
|
|
388
|
+
ens_conserved *= en_conv
|
|
389
|
+
mean_conserved = ens_conserved.mean()
|
|
390
|
+
ens_conserved -= mean_conserved
|
|
391
|
+
ax1.plot(dts, ens_conserved)
|
|
392
|
+
ax1.axhline(0, ls="--", c="k")
|
|
393
|
+
ax1.set_ylabel(r"$E_\\mathrm{cons.} - \overline{E}_\\mathrm{cons.}$ / {en_unit}")
|
|
394
|
+
ax1.set_title("Conserved quantity")
|
|
395
|
+
|
|
396
|
+
ax2.plot(dts, Ts, label="Current")
|
|
397
|
+
ax2.plot(dts, T_avgs, ls="--", c="orange", label="Average")
|
|
398
|
+
ax2.axhline(T_target, ls="--", c="k", label="Target")
|
|
399
|
+
ax2.legend()
|
|
400
|
+
ax2.set_title(f"mean(T) = {Ts.mean():.2f} K")
|
|
401
|
+
ax2.set_xlabel(r"$\Delta t$ / fs")
|
|
402
|
+
ax2.set_ylabel("T / K")
|
|
403
|
+
|
|
404
|
+
plt.tight_layout()
|
|
405
|
+
plt.show()
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def plot_gau(gau_fns, num=50):
|
|
409
|
+
print("Assuming constant Gaussian s & w!")
|
|
410
|
+
|
|
411
|
+
assert (
|
|
412
|
+
0 < len(gau_fns) < 3
|
|
413
|
+
), "Currently, only plotting of 1 or 2 collective variables is possible!"
|
|
414
|
+
|
|
415
|
+
gaussians = list()
|
|
416
|
+
centers = list()
|
|
417
|
+
grids = list()
|
|
418
|
+
for gau_fn in gau_fns:
|
|
419
|
+
gau_data = np.loadtxt(gau_fn)
|
|
420
|
+
gau_centers = gau_data[:, 3]
|
|
421
|
+
_, s, w, _ = gau_data[0]
|
|
422
|
+
gau = Gaussian(w=w, s=s)
|
|
423
|
+
print(f"Successfully loaded '{gau_fn}' with w={w:.6f}, s={s:.6f}")
|
|
424
|
+
gaussians.append(gau)
|
|
425
|
+
min_ = gau_centers.min()
|
|
426
|
+
max_ = gau_centers.max()
|
|
427
|
+
diff = abs(max_ - min_)
|
|
428
|
+
centers.append(gau_centers)
|
|
429
|
+
grid = np.linspace(min_, max_, num=num)
|
|
430
|
+
grids.append(grid)
|
|
431
|
+
print(f"\tmin={min_:.4f}, max={max_:.4f}, Δ={diff:.4f}")
|
|
432
|
+
|
|
433
|
+
def eval_gaussians(coords):
|
|
434
|
+
value = 0.0
|
|
435
|
+
for i, gau in enumerate(gaussians):
|
|
436
|
+
value += gau.value(coords=coords[i], x0=centers[i])
|
|
437
|
+
return value
|
|
438
|
+
|
|
439
|
+
fig, ax = plt.subplots()
|
|
440
|
+
|
|
441
|
+
en_conv, en_unit = get_en_conv()
|
|
442
|
+
if len(gau_fns) == 1:
|
|
443
|
+
grid = grids[0]
|
|
444
|
+
ens = -np.array([eval_gaussians(x) for x in grid[:, None]]) * en_conv
|
|
445
|
+
ens -= ens.min()
|
|
446
|
+
ax.plot(grid, ens)
|
|
447
|
+
ax.set_xlabel(f"CV0, {gau_fns[0]}")
|
|
448
|
+
ax.set_ylabel(rf"$\Delta F$ / {en_unit}")
|
|
449
|
+
elif len(gau_fns) == 2:
|
|
450
|
+
grid0, grid1 = grids
|
|
451
|
+
X, Y = np.meshgrid(grid0, grid1)
|
|
452
|
+
xy_flat = np.stack((X.flatten(), Y.flatten()), axis=1)
|
|
453
|
+
ens = (
|
|
454
|
+
-np.array([eval_gaussians(xy) for xy in xy_flat]).reshape(num, num)
|
|
455
|
+
* en_conv
|
|
456
|
+
)
|
|
457
|
+
ens -= ens.min()
|
|
458
|
+
levels = np.linspace(ens.min(), 0.75 * ens.max(), num=15)
|
|
459
|
+
# contour = ax.contour(X, Y, ens, levels=levels)
|
|
460
|
+
_ = ax.contourf(X, Y, ens, levels=levels)
|
|
461
|
+
# plt.clabel(contour, inline=True, fmt="%1.1f", fontsize=10, colors="white", levels=levels)
|
|
462
|
+
ax.set_xlabel(f"CV0, {gau_fns[0]}")
|
|
463
|
+
ax.set_ylabel(f"CV1, {gau_fns[1]}")
|
|
464
|
+
plt.show()
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def plot_overlaps(h5, thresh=0.1):
|
|
468
|
+
with h5py.File(h5, "r") as handle:
|
|
469
|
+
overlaps = handle["overlap_matrices"][:]
|
|
470
|
+
roots = handle["roots"][:]
|
|
471
|
+
calculated_roots = handle["calculated_roots"][:]
|
|
472
|
+
ref_cycles = handle["ref_cycles"][:]
|
|
473
|
+
ref_roots = handle["ref_roots"][:]
|
|
474
|
+
try:
|
|
475
|
+
ovlp_type = handle.attrs["ovlp_type"]
|
|
476
|
+
ovlp_with = handle.attrs["ovlp_with"]
|
|
477
|
+
# The old way is handled below. Newer pysis versions store ovlp_type/ovlp_with
|
|
478
|
+
# in attrs.
|
|
479
|
+
except KeyError:
|
|
480
|
+
ovlp_type = handle["ovlp_type"][()].decode()
|
|
481
|
+
ovlp_with = handle["ovlp_with"][()].decode()
|
|
482
|
+
try:
|
|
483
|
+
cdd_img_fns = handle["cdd_imgs"][:]
|
|
484
|
+
except KeyError:
|
|
485
|
+
print(f"Couldn't find image data in '{h5}'.")
|
|
486
|
+
try:
|
|
487
|
+
with open(CDD_PNG_FNS) as handle:
|
|
488
|
+
cdd_img_fns = handle.read().split()
|
|
489
|
+
print(f"Found image data in '{CDD_PNG_FNS}'")
|
|
490
|
+
except FileNotFoundError:
|
|
491
|
+
cdd_img_fns = None
|
|
492
|
+
cdd_imgs = None
|
|
493
|
+
if cdd_img_fns is not None:
|
|
494
|
+
try:
|
|
495
|
+
cdd_imgs = [mpimg.imread(fn) for fn in cdd_img_fns]
|
|
496
|
+
except FileNotFoundError:
|
|
497
|
+
png_paths = [Path(fn.decode()).name for fn in cdd_img_fns]
|
|
498
|
+
cdd_imgs = [mpimg.imread(fn) for fn in png_paths]
|
|
499
|
+
print(f"Found rendered {len(cdd_imgs)} CDD images.")
|
|
500
|
+
|
|
501
|
+
overlaps[np.abs(overlaps) < thresh] = np.nan
|
|
502
|
+
print(f"Overlap type: {ovlp_type}")
|
|
503
|
+
print(f"Overlap with: {ovlp_with}")
|
|
504
|
+
print(f"Found {len(overlaps)} overlap matrices.")
|
|
505
|
+
print(f"Roots: {roots}")
|
|
506
|
+
print(f"Reference cycles: {ref_cycles}")
|
|
507
|
+
print(f"Reference roots: {ref_roots}")
|
|
508
|
+
print()
|
|
509
|
+
print("Key-bindings:")
|
|
510
|
+
print("i: switch between current and first cycle.")
|
|
511
|
+
print("e: switch between current and last cycle.")
|
|
512
|
+
|
|
513
|
+
fig, ax = plt.subplots()
|
|
514
|
+
|
|
515
|
+
n_states = overlaps[0].shape[0]
|
|
516
|
+
|
|
517
|
+
def draw(i):
|
|
518
|
+
fig.clf()
|
|
519
|
+
if cdd_imgs is not None:
|
|
520
|
+
ax = fig.add_subplot(121)
|
|
521
|
+
ax1 = fig.add_subplot(122)
|
|
522
|
+
else:
|
|
523
|
+
ax = fig.add_subplot(111)
|
|
524
|
+
ax1 = None
|
|
525
|
+
o = np.abs(overlaps[i])
|
|
526
|
+
ax.imshow(o, vmin=0, vmax=1)
|
|
527
|
+
ax.grid(color="#CCCCCC", linestyle="--", linewidth=1)
|
|
528
|
+
ax.set_xticks(np.arange(n_states, dtype=np.int))
|
|
529
|
+
ax.set_yticks(np.arange(n_states, dtype=np.int))
|
|
530
|
+
# set_ylim is needed, otherwise set_yticks drastically shrinks the plot
|
|
531
|
+
ax.set_ylim(n_states - 0.5, -0.5)
|
|
532
|
+
ax.set_xlabel("new roots")
|
|
533
|
+
ax.set_ylabel("reference roots")
|
|
534
|
+
for (l, k), value in np.ndenumerate(o):
|
|
535
|
+
if np.isnan(value):
|
|
536
|
+
continue
|
|
537
|
+
value_str = f"{abs(value):.2f}"
|
|
538
|
+
ax.text(k, l, value_str, ha="center", va="center")
|
|
539
|
+
j, k = ref_cycles[i], i + 1
|
|
540
|
+
ref_root = ref_roots[i]
|
|
541
|
+
ref_ind = ref_root - 1
|
|
542
|
+
if ovlp_type == "wf":
|
|
543
|
+
ref_ind += 1
|
|
544
|
+
old_root = calculated_roots[i + 1]
|
|
545
|
+
new_root = roots[i + 1]
|
|
546
|
+
ref_overlaps = o[ref_ind]
|
|
547
|
+
argmax = np.nanargmax(ref_overlaps)
|
|
548
|
+
xy = (argmax - 0.5, ref_ind - 0.5)
|
|
549
|
+
highlight = Rectangle(xy, 1, 1, fill=False, color="red", lw="4")
|
|
550
|
+
ax.add_artist(highlight)
|
|
551
|
+
if ax1:
|
|
552
|
+
ax1.imshow(cdd_imgs[i])
|
|
553
|
+
fig.suptitle(
|
|
554
|
+
f"overlap {i:03d}\n"
|
|
555
|
+
f"{ovlp_type} overlap between {j:03d} and {k:03d}\n"
|
|
556
|
+
f"old root: {old_root}, new root: {new_root}"
|
|
557
|
+
)
|
|
558
|
+
fig.canvas.draw()
|
|
559
|
+
|
|
560
|
+
draw(0)
|
|
561
|
+
|
|
562
|
+
i = 0
|
|
563
|
+
i_backup = i
|
|
564
|
+
i_last = len(overlaps) - 1
|
|
565
|
+
|
|
566
|
+
def press(event):
|
|
567
|
+
nonlocal i
|
|
568
|
+
nonlocal i_backup
|
|
569
|
+
if event.key == "left":
|
|
570
|
+
i = max(0, i - 1)
|
|
571
|
+
elif event.key == "right":
|
|
572
|
+
i = min(i_last, i + 1)
|
|
573
|
+
# Switch between current and first cycle
|
|
574
|
+
elif event.key == "i":
|
|
575
|
+
if i == 0:
|
|
576
|
+
# Restore previous cycle
|
|
577
|
+
i = i_backup
|
|
578
|
+
else:
|
|
579
|
+
# Save current i and jump to the first cycle/image
|
|
580
|
+
i_backup = i
|
|
581
|
+
i = 0
|
|
582
|
+
# Switch between current and last cycle
|
|
583
|
+
elif event.key == "e":
|
|
584
|
+
if i == i_last:
|
|
585
|
+
# Restore previous cycle
|
|
586
|
+
i = i_backup
|
|
587
|
+
else:
|
|
588
|
+
# Save current i and jump to the first cycle/image
|
|
589
|
+
i_backup = i
|
|
590
|
+
i = i_last
|
|
591
|
+
else:
|
|
592
|
+
return
|
|
593
|
+
draw(i)
|
|
594
|
+
|
|
595
|
+
fig.canvas.mpl_connect("key_press_event", press)
|
|
596
|
+
|
|
597
|
+
plt.tight_layout()
|
|
598
|
+
plt.show()
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def render_cdds(h5):
|
|
602
|
+
with h5py.File(h5) as handle:
|
|
603
|
+
cdd_cubes = handle["cdd_cubes"][:].astype(str)
|
|
604
|
+
orient = handle["orient"][()].decode()
|
|
605
|
+
cdd_cubes = [Path(cub) for cub in cdd_cubes]
|
|
606
|
+
print(f"Found {len(cdd_cubes)} CDD cube filenames in {h5}")
|
|
607
|
+
# Check if cubes exist
|
|
608
|
+
non_existant_cubes = [cub for cub in cdd_cubes if not cub.exists()]
|
|
609
|
+
existing_cubes = [str(cub) for cub in set(cdd_cubes) - set(non_existant_cubes)]
|
|
610
|
+
if any(non_existant_cubes):
|
|
611
|
+
print("Couldn't find cubes:")
|
|
612
|
+
print("\n".join(["\t" + str(cub) for cub in non_existant_cubes]))
|
|
613
|
+
print("Dropping full path and looking only for cube names.")
|
|
614
|
+
cub_names = [cub.name for cub in non_existant_cubes]
|
|
615
|
+
_ = [cub for cub in cub_names if Path(cub).exists()]
|
|
616
|
+
existing_cubes = existing_cubes + _
|
|
617
|
+
cdd_cubes = existing_cubes
|
|
618
|
+
|
|
619
|
+
# Create list of all final PNG filenames
|
|
620
|
+
png_fns = [Path(cube).with_suffix(".png") for cube in cdd_cubes]
|
|
621
|
+
# Check which cubes are already rendered
|
|
622
|
+
png_stems = [png.stem for png in png_fns if png.exists()]
|
|
623
|
+
print(f"{len(png_stems)} cubes seem already rendered.")
|
|
624
|
+
|
|
625
|
+
# Only render cubes that are not yet rendered
|
|
626
|
+
cdd_cubes = [cube for cube in cdd_cubes if Path(cube).stem not in png_stems]
|
|
627
|
+
print(f"Rendering {len(cdd_cubes)} CDD cubes.")
|
|
628
|
+
|
|
629
|
+
for i, cube in enumerate(cdd_cubes):
|
|
630
|
+
print(f"Rendering cube {i+1:03d}/{len(cdd_cubes):03d}")
|
|
631
|
+
_ = render_cdd_cube(cube, orient=orient)
|
|
632
|
+
joined = "\n".join([str(fn) for fn in png_fns])
|
|
633
|
+
with open(CDD_PNG_FNS, "w") as handle:
|
|
634
|
+
handle.write(joined)
|
|
635
|
+
print("Rendered PNGs:")
|
|
636
|
+
print(joined)
|
|
637
|
+
print(f"Wrote list of rendered PNGs to '{CDD_PNG_FNS}'")
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def plot_afir(h5_fn="afir.h5", h5_group="afir"):
|
|
641
|
+
|
|
642
|
+
h5_fns = (h5_fn, Path(OUT_DIR_DEFAULT) / h5_fn)
|
|
643
|
+
for h5_fn in h5_fns:
|
|
644
|
+
print(f"Trying to open '{h5_fn}' ... ", end="")
|
|
645
|
+
try:
|
|
646
|
+
with h5py.File(h5_fn, "r") as handle:
|
|
647
|
+
group = handle[h5_group]
|
|
648
|
+
cycles = group.attrs["cur_cycle"] + 1
|
|
649
|
+
afir_ens = group["energy"][:cycles]
|
|
650
|
+
true_ens = group["true_energy"][:cycles]
|
|
651
|
+
afir_forces = group["forces"][:cycles]
|
|
652
|
+
true_forces = group["true_forces"][:cycles]
|
|
653
|
+
print("done.")
|
|
654
|
+
break
|
|
655
|
+
except FileNotFoundError:
|
|
656
|
+
print("file not found.")
|
|
657
|
+
continue
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
en_conv, en_unit = get_en_conv()
|
|
661
|
+
afir_ens *= en_conv
|
|
662
|
+
afir_ens -= afir_ens.min()
|
|
663
|
+
true_ens *= en_conv
|
|
664
|
+
true_ens -= true_ens.min()
|
|
665
|
+
afir_forces = np.linalg.norm(afir_forces, axis=1)
|
|
666
|
+
true_forces = np.linalg.norm(true_forces, axis=1)
|
|
667
|
+
|
|
668
|
+
fig, (en_ax, forces_ax) = plt.subplots(nrows=2, sharex=True)
|
|
669
|
+
|
|
670
|
+
style1 = "r--"
|
|
671
|
+
style2 = "g--"
|
|
672
|
+
style3 = "bo-"
|
|
673
|
+
|
|
674
|
+
l1 = en_ax.plot(afir_ens, style1, label="AFIR")
|
|
675
|
+
l2 = en_ax.plot(true_ens, style2, label="True")
|
|
676
|
+
en_ax2 = en_ax.twinx()
|
|
677
|
+
l3 = en_ax2.plot(true_ens + afir_ens, style3, label="Sum")
|
|
678
|
+
en_ax2.tick_params(axis="y", labelcolor="blue")
|
|
679
|
+
|
|
680
|
+
lines = l1 + l2 + l3
|
|
681
|
+
labels = [l.get_label() for l in lines]
|
|
682
|
+
en_ax.legend(lines, labels, loc=0)
|
|
683
|
+
|
|
684
|
+
en_ax.set_title("Energies")
|
|
685
|
+
en_ax.set_ylabel(DE_LABEL)
|
|
686
|
+
|
|
687
|
+
forces_ax.set_title("||Forces||")
|
|
688
|
+
l1 = forces_ax.plot(afir_forces, style1, label="AFIR")
|
|
689
|
+
l2 = forces_ax.plot(true_forces, style2, label="True")
|
|
690
|
+
|
|
691
|
+
forces_ax2 = forces_ax.twinx()
|
|
692
|
+
l3 = forces_ax2.plot(true_forces + afir_forces, style3, label="Sum")
|
|
693
|
+
forces_ax2.tick_params(axis="y", labelcolor="blue")
|
|
694
|
+
|
|
695
|
+
lines = l1 + l2 + l3
|
|
696
|
+
labels = [l.get_label() for l in lines]
|
|
697
|
+
forces_ax.legend(lines, labels, loc=0)
|
|
698
|
+
forces_ax.set_xlabel("Cycle")
|
|
699
|
+
forces_ax.set_ylabel("$E_h$ Bohr⁻¹")
|
|
700
|
+
|
|
701
|
+
peak_inds, _ = peakdetect(true_ens, lookahead=2)
|
|
702
|
+
if peak_inds:
|
|
703
|
+
print("Peaks:")
|
|
704
|
+
print(f"\tCycle: Energy / {en_unit}")
|
|
705
|
+
print()
|
|
706
|
+
for at, energy in peak_inds:
|
|
707
|
+
print(f"\t{at}: {energy:.2f}")
|
|
708
|
+
|
|
709
|
+
try:
|
|
710
|
+
peak_xs, peak_ys = zip(*peak_inds)
|
|
711
|
+
highest = np.argmax(peak_ys)
|
|
712
|
+
|
|
713
|
+
en_ax.scatter(peak_xs, peak_ys, s=100, marker="X", c="k", zorder=10)
|
|
714
|
+
en_ax.scatter(
|
|
715
|
+
peak_xs[highest], peak_ys[highest], s=150, marker="X", c="k", zorder=10
|
|
716
|
+
)
|
|
717
|
+
en_ax.axvline(peak_xs[highest], c="k", ls="--")
|
|
718
|
+
forces_ax.axvline(peak_xs[highest], c="k", ls="--")
|
|
719
|
+
except ValueError as err:
|
|
720
|
+
print("Peak-detection failed!")
|
|
721
|
+
|
|
722
|
+
# fig.legend(loc="upper right")
|
|
723
|
+
plt.tight_layout()
|
|
724
|
+
plt.show()
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
def parse_args(args):
|
|
728
|
+
parser = argparse.ArgumentParser()
|
|
729
|
+
parser.add_argument("--h5_fn", default="overlap_data.h5")
|
|
730
|
+
parser.add_argument("--h5_group", default="opt", help="HDF5 group to plot.")
|
|
731
|
+
parser.add_argument("--orient", default="")
|
|
732
|
+
parser.add_argument(
|
|
733
|
+
"--kcal", action="store_true", help="Use kcal mol⁻¹ instead of kJ mol⁻¹."
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
737
|
+
group.add_argument(
|
|
738
|
+
"--cosforces",
|
|
739
|
+
"--cf",
|
|
740
|
+
action="store_true",
|
|
741
|
+
help="Plot image forces along a COS.",
|
|
742
|
+
)
|
|
743
|
+
group.add_argument(
|
|
744
|
+
"--cosens", "--ce", action="store_true", help="Plot COS energies."
|
|
745
|
+
)
|
|
746
|
+
group.add_argument(
|
|
747
|
+
"--all_energies",
|
|
748
|
+
"-a",
|
|
749
|
+
action="store_true",
|
|
750
|
+
help="Plot ground and excited state energies from 'overlap_data.h5'.",
|
|
751
|
+
)
|
|
752
|
+
group.add_argument(
|
|
753
|
+
"--afir",
|
|
754
|
+
action="store_true",
|
|
755
|
+
help="Plot AFIR and true -energies and -forces from an AFIR calculation.",
|
|
756
|
+
)
|
|
757
|
+
group.add_argument("--opt", action="store_true", help="Plot optimization progress.")
|
|
758
|
+
group.add_argument("--irc", action="store_true", help="Plot IRC progress.")
|
|
759
|
+
group.add_argument("--overlaps", "-o", action="store_true")
|
|
760
|
+
group.add_argument("--render_cdds", action="store_true")
|
|
761
|
+
group.add_argument("--h5_list", default=None, help="List groups in HDF5 file.")
|
|
762
|
+
group.add_argument("--md", action="store_true", help="Plot MD.")
|
|
763
|
+
group.add_argument("--gau", nargs="*")
|
|
764
|
+
group.add_argument("--scan", action="store_true")
|
|
765
|
+
group.add_argument(
|
|
766
|
+
"--trj", help="Plot energy values from the comments of a " "_trj.xyz file."
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
return parser.parse_args(args)
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def plot_opt(h5_fn="optimization.h5", h5_group="opt"):
|
|
773
|
+
with h5py.File(h5_fn, "r") as handle:
|
|
774
|
+
try:
|
|
775
|
+
group = handle[h5_group]
|
|
776
|
+
except KeyError:
|
|
777
|
+
groups = list(handle.keys())
|
|
778
|
+
groups_str = "\t" + "\n\t".join(groups)
|
|
779
|
+
print(
|
|
780
|
+
f"Could not find group '{h5_group}'!\nAvailable groups are:\n{groups_str}\n"
|
|
781
|
+
f"Use '--h5_group [group]' to plot a different group."
|
|
782
|
+
)
|
|
783
|
+
if groups:
|
|
784
|
+
group = handle[groups[0]]
|
|
785
|
+
print(f"Using first group '{group}'.")
|
|
786
|
+
else:
|
|
787
|
+
return
|
|
788
|
+
|
|
789
|
+
cur_cycle = group.attrs["cur_cycle"]
|
|
790
|
+
is_cos = group.attrs["is_cos"]
|
|
791
|
+
is_converged = group.attrs["is_converged"]
|
|
792
|
+
coord_type = group.attrs["coord_type"]
|
|
793
|
+
|
|
794
|
+
ens = group["energies"][:cur_cycle]
|
|
795
|
+
max_forces = group["max_forces"][:cur_cycle]
|
|
796
|
+
rms_forces = group["rms_forces"][:cur_cycle]
|
|
797
|
+
max_force_thresh = group.attrs["max_force_thresh"]
|
|
798
|
+
rms_force_thresh = group.attrs["rms_force_thresh"]
|
|
799
|
+
|
|
800
|
+
en_conv, en_unit = get_en_conv()
|
|
801
|
+
ens -= ens.min()
|
|
802
|
+
ens *= en_conv
|
|
803
|
+
if is_cos:
|
|
804
|
+
text = textwrap.wrap(
|
|
805
|
+
"COS optimization detected. Plotting total energy of all images "
|
|
806
|
+
"in every cycle. Results from optimizing growing COS methods can "
|
|
807
|
+
"be plotted but the plots are not really useful as the varying "
|
|
808
|
+
"number of images is not considered.",
|
|
809
|
+
width=80,
|
|
810
|
+
)
|
|
811
|
+
print("\n".join(text))
|
|
812
|
+
ens = ens.sum(axis=1)
|
|
813
|
+
force_unit = get_force_unit(coord_type)
|
|
814
|
+
|
|
815
|
+
ax_kwargs = {
|
|
816
|
+
"marker": "o",
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, sharex=True)
|
|
820
|
+
|
|
821
|
+
ax0.plot(ens, **ax_kwargs)
|
|
822
|
+
ax0.set_ylabel(DE_LABEL)
|
|
823
|
+
|
|
824
|
+
ax1.plot(max_forces, **ax_kwargs)
|
|
825
|
+
ax1.axhline(max_force_thresh, c="k", ls="--", label="Threshold")
|
|
826
|
+
ax1.set_yscale("log")
|
|
827
|
+
ax1.set_title("max(forces)")
|
|
828
|
+
ax1.set_ylabel(force_unit)
|
|
829
|
+
ax1.legend()
|
|
830
|
+
|
|
831
|
+
ax2.plot(rms_forces, **ax_kwargs)
|
|
832
|
+
ax2.axhline(rms_force_thresh, c="k", ls="--", label="Threshold")
|
|
833
|
+
ax2.set_yscale("log")
|
|
834
|
+
ax2.set_title("rms(forces)")
|
|
835
|
+
ax2.set_xlabel("Cycle")
|
|
836
|
+
ax2.set_ylabel(force_unit)
|
|
837
|
+
ax2.legend()
|
|
838
|
+
|
|
839
|
+
title = f"{h5_fn}/{h5_group}, converged={is_converged}"
|
|
840
|
+
fig.suptitle(title, y=0.999)
|
|
841
|
+
|
|
842
|
+
plt.tight_layout()
|
|
843
|
+
plt.show()
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
def plot_irc():
|
|
847
|
+
cwd = Path(".")
|
|
848
|
+
h5s = cwd.glob("*irc_data.h5")
|
|
849
|
+
for h5 in h5s:
|
|
850
|
+
type_ = h5.name.split("_")[0]
|
|
851
|
+
title = f"{type_.capitalize()} IRC data"
|
|
852
|
+
_ = plot_irc_h5(h5, title)
|
|
853
|
+
plt.show()
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def plot_irc_h5(h5, title=None):
|
|
857
|
+
print(f"Reading IRC data {h5}")
|
|
858
|
+
with h5py.File(h5, "r") as handle:
|
|
859
|
+
mw_coords = handle["mw_coords"][:]
|
|
860
|
+
energies = handle["energies"][:]
|
|
861
|
+
gradients = handle["gradients"][:]
|
|
862
|
+
rms_grad_thresh = handle["rms_grad_thresh"][()]
|
|
863
|
+
|
|
864
|
+
try:
|
|
865
|
+
ts_index = handle["ts_index"][()]
|
|
866
|
+
except KeyError:
|
|
867
|
+
ts_index = None
|
|
868
|
+
|
|
869
|
+
sizes = [dataset.shape[0] for dataset in (mw_coords, energies, gradients)]
|
|
870
|
+
size0 = sizes[0]
|
|
871
|
+
assert all([size == size0 for size in sizes])
|
|
872
|
+
print(f"\tFound {size0} IRC points")
|
|
873
|
+
|
|
874
|
+
en_conv, en_unit = get_en_conv()
|
|
875
|
+
energies -= energies[0]
|
|
876
|
+
energies *= en_conv
|
|
877
|
+
|
|
878
|
+
cds = np.linalg.norm(mw_coords - mw_coords[0], axis=1)
|
|
879
|
+
rms_grads = np.sqrt(np.mean(gradients ** 2, axis=1))
|
|
880
|
+
max_grads = np.abs(gradients).max(axis=1)
|
|
881
|
+
|
|
882
|
+
fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, sharex=True)
|
|
883
|
+
|
|
884
|
+
plt_kwargs = {
|
|
885
|
+
"linestyle": "-",
|
|
886
|
+
"marker": "o",
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
ax0.plot(cds, energies, **plt_kwargs)
|
|
890
|
+
ax0.set_title("energy change")
|
|
891
|
+
ax0.set_ylabel(DE_LABEL)
|
|
892
|
+
|
|
893
|
+
ax1.plot(cds, rms_grads, **plt_kwargs)
|
|
894
|
+
ax1.axhline(rms_grad_thresh, linestyle="--", color="k")
|
|
895
|
+
ax1.set_title("rms(gradient)")
|
|
896
|
+
ax1.set_ylabel("$E_h$ Bohr⁻¹")
|
|
897
|
+
|
|
898
|
+
ax2.plot(cds, max_grads, **plt_kwargs)
|
|
899
|
+
ax2.set_title("max(gradient)")
|
|
900
|
+
ax2.set_xlabel("IRC / amu$^{\\frac{1}{2}}$ Bohr")
|
|
901
|
+
ax2.set_ylabel("$E_h$ Bohr⁻¹")
|
|
902
|
+
|
|
903
|
+
if ts_index:
|
|
904
|
+
x = cds[ts_index]
|
|
905
|
+
for ax, arr in ((ax0, energies), (ax1, rms_grads), (ax2, max_grads)):
|
|
906
|
+
xy = (x, arr[ts_index])
|
|
907
|
+
ax.annotate("TS", xy, fontsize=12, fontweight="bold")
|
|
908
|
+
|
|
909
|
+
if title:
|
|
910
|
+
fig.suptitle(title)
|
|
911
|
+
else:
|
|
912
|
+
fig.tight_layout()
|
|
913
|
+
|
|
914
|
+
return fig, (ax0, ax1, ax2)
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
def plot_scan(h5_fn="scan.h5"):
|
|
918
|
+
with h5py.File(h5_fn, "r") as handle:
|
|
919
|
+
groups = list()
|
|
920
|
+
energies = list()
|
|
921
|
+
for k in handle.keys():
|
|
922
|
+
group = handle[k]
|
|
923
|
+
groups.append(k)
|
|
924
|
+
energies.append(group["energies"][:])
|
|
925
|
+
print(f"Found {len(groups)} groups in '{h5_fn}'.")
|
|
926
|
+
|
|
927
|
+
en_conv, _ = get_en_conv
|
|
928
|
+
for group, ens in zip(groups, energies):
|
|
929
|
+
ens -= ens.min()
|
|
930
|
+
ens *= en_conv
|
|
931
|
+
fig, ax = plt.subplots()
|
|
932
|
+
ax.plot(ens, "o-")
|
|
933
|
+
ax.set_xlabel("Scan point")
|
|
934
|
+
ax.set_ylabel(DE_LABEL)
|
|
935
|
+
ax.set_title(group)
|
|
936
|
+
plt.show()
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
def plot_trj_energies(trj):
|
|
940
|
+
"""Parse comments of .xyz/_trj.xyz as energies and plot."""
|
|
941
|
+
atoms_coords, comments = parse_xyz(trj, with_comment=True)
|
|
942
|
+
try:
|
|
943
|
+
energies = np.array(comments, dtype=float)
|
|
944
|
+
except ValueError as err:
|
|
945
|
+
print("Could not convert comments to energies!\n")
|
|
946
|
+
raise err
|
|
947
|
+
en_conv, en_unit = get_en_conv()
|
|
948
|
+
energies -= energies.min()
|
|
949
|
+
energies *= en_conv
|
|
950
|
+
fig, ax = plt.subplots()
|
|
951
|
+
ax.plot(energies)
|
|
952
|
+
highlights = [0, energies.argmax(), energies.size - 1]
|
|
953
|
+
highlight_ens = energies[highlights]
|
|
954
|
+
ax.scatter(highlights, highlight_ens, s=50)
|
|
955
|
+
ax.set_xlabel("Step")
|
|
956
|
+
ax.set_ylabel(DE_LABEL)
|
|
957
|
+
ax.set_title(trj)
|
|
958
|
+
plt.show()
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
def list_h5_groups(h5_fn):
|
|
962
|
+
with h5py.File(h5_fn, "r") as handle:
|
|
963
|
+
groups = list(handle.keys())
|
|
964
|
+
|
|
965
|
+
print(f"Found {len(groups)} groups in '{h5_fn}'\n")
|
|
966
|
+
for i, grp in enumerate(groups):
|
|
967
|
+
print(f"\t{i:02d}: {grp}")
|
|
968
|
+
|
|
969
|
+
if groups:
|
|
970
|
+
print("\nAvailable groups can be selected by '--h5_group [name]'.")
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
def run():
|
|
974
|
+
args = parse_args(sys.argv[1:])
|
|
975
|
+
|
|
976
|
+
h5_fn = Path(args.h5_fn)
|
|
977
|
+
|
|
978
|
+
global get_en_conv
|
|
979
|
+
|
|
980
|
+
def get_en_conv():
|
|
981
|
+
if args.kcal:
|
|
982
|
+
conv, label = AU2KCALPERMOL, "kcal mol⁻¹"
|
|
983
|
+
else:
|
|
984
|
+
conv, label = AU2KJPERMOL, "kJ mol⁻¹"
|
|
985
|
+
return conv, label
|
|
986
|
+
|
|
987
|
+
global DE_LABEL
|
|
988
|
+
DE_LABEL = rf"$\Delta$E / {get_en_conv()[1]}"
|
|
989
|
+
|
|
990
|
+
if args.all_energies or args.overlaps:
|
|
991
|
+
if not h5_fn.exists():
|
|
992
|
+
if (tmp_fn := Path(OUT_DIR_DEFAULT) / h5_fn).exists():
|
|
993
|
+
h5_fn = tmp_fn
|
|
994
|
+
print(f"Loading overlap data from '{h5_fn}'.")
|
|
995
|
+
|
|
996
|
+
# Optimization
|
|
997
|
+
if args.h5_list:
|
|
998
|
+
list_h5_groups(args.h5_list)
|
|
999
|
+
if args.opt:
|
|
1000
|
+
plot_opt(h5_group=args.h5_group)
|
|
1001
|
+
# COS specific
|
|
1002
|
+
elif args.cosens:
|
|
1003
|
+
plot_cos_energies(h5_group=args.h5_group)
|
|
1004
|
+
elif args.cosforces:
|
|
1005
|
+
plot_cos_forces(h5_group=args.h5_group)
|
|
1006
|
+
# AFIR
|
|
1007
|
+
elif args.afir:
|
|
1008
|
+
plot_afir()
|
|
1009
|
+
# IRC related
|
|
1010
|
+
elif args.irc:
|
|
1011
|
+
plot_irc()
|
|
1012
|
+
# Overlap calculator related
|
|
1013
|
+
elif args.all_energies:
|
|
1014
|
+
plot_all_energies(h5=h5_fn)
|
|
1015
|
+
elif args.overlaps:
|
|
1016
|
+
plot_overlaps(h5=h5_fn)
|
|
1017
|
+
# MD related
|
|
1018
|
+
elif args.md:
|
|
1019
|
+
plot_md()
|
|
1020
|
+
elif args.gau:
|
|
1021
|
+
plot_gau(args.gau)
|
|
1022
|
+
elif args.scan:
|
|
1023
|
+
plot_scan()
|
|
1024
|
+
elif args.render_cdds:
|
|
1025
|
+
render_cdds(h5=h5_fn)
|
|
1026
|
+
elif args.trj:
|
|
1027
|
+
plot_trj_energies(args.trj)
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
if __name__ == "__main__":
|
|
1031
|
+
run()
|