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,127 @@
|
|
|
1
|
+
import itertools as it
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import rmsd as rmsd
|
|
5
|
+
from scipy.optimize import linear_sum_assignment
|
|
6
|
+
from scipy.spatial.distance import cdist
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def match_geom_atoms(ref_geom, geom_to_match, hydrogen=True):
|
|
10
|
+
"""Apply the hungarian method to geom_to_match.
|
|
11
|
+
|
|
12
|
+
Uses the scipy implementation of the Hungarian method/
|
|
13
|
+
Kuhn-Munkres algorithm in scipy.optimize.linear_sum_assignment.
|
|
14
|
+
|
|
15
|
+
See
|
|
16
|
+
[1] 10.1021/ci400534h
|
|
17
|
+
[2] 10.1021/acs.jcim.6b00516
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
assert len(ref_geom.atoms) == len(geom_to_match.atoms), \
|
|
21
|
+
"Atom numbers don't match!"
|
|
22
|
+
|
|
23
|
+
ref_coords, _ = ref_geom.coords_by_type
|
|
24
|
+
coords_to_match, inds_to_match = geom_to_match.coords_by_type
|
|
25
|
+
atoms = ref_coords.keys()
|
|
26
|
+
|
|
27
|
+
# The new geometry that will contain the perhaps reordered
|
|
28
|
+
# coordinates.
|
|
29
|
+
geom_matched = geom_to_match.copy()
|
|
30
|
+
for atom in atoms:
|
|
31
|
+
# Don't match hydrogens if not requested
|
|
32
|
+
if atom.lower() == "h" and not hydrogen:
|
|
33
|
+
continue
|
|
34
|
+
ref_coords_for_atom = ref_coords[atom]
|
|
35
|
+
coords_to_match_for_atom = coords_to_match[atom]
|
|
36
|
+
# Pairwise distances between two collections
|
|
37
|
+
# Atoms of ref_geom are along the rows, atoms of geom_to_match
|
|
38
|
+
# along the columns.
|
|
39
|
+
cd = cdist(ref_coords_for_atom, coords_to_match_for_atom)
|
|
40
|
+
# Hungarian method, row_inds are returned already sorted, so
|
|
41
|
+
# we only have to consider col_inds.
|
|
42
|
+
row_inds, col_inds = linear_sum_assignment(cd)
|
|
43
|
+
# Resort the coordinates
|
|
44
|
+
new_coords_for_atom = coords_to_match_for_atom[col_inds]
|
|
45
|
+
# Original indices of the coordinates in the full coords array
|
|
46
|
+
# in the Geometry object.
|
|
47
|
+
old_inds = inds_to_match[atom]
|
|
48
|
+
# Update coordinates and modify the array directly
|
|
49
|
+
c3d = geom_matched.coords3d
|
|
50
|
+
c3d[old_inds] = new_coords_for_atom
|
|
51
|
+
return geom_matched
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def apply_transform(coords3d, swap, reflection):
|
|
55
|
+
"""Apply axis swaps and reflections to a coordinate array."""
|
|
56
|
+
# Swap axes
|
|
57
|
+
coords3d = coords3d[:, swap]
|
|
58
|
+
# Reflect
|
|
59
|
+
coords3d *= reflection
|
|
60
|
+
return coords3d
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def matched_rmsd(geom1, geom2, thresh=5e-2):
|
|
64
|
+
"""RMSD for optimally aligned and matched geometries.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
matched_rmsd : float
|
|
69
|
+
RMSD of optimally aligned and matched geometries.
|
|
70
|
+
matched_geoms : tuple(Geometry, Geometry)
|
|
71
|
+
Tuple of the optimally aligned and matched geometries.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
# Work on copies of the Geometries, as calling standard_orientation
|
|
75
|
+
# moves their coordinates.
|
|
76
|
+
geom1_copy = geom1.copy()
|
|
77
|
+
geom1_copy.standard_orientation()
|
|
78
|
+
coords3d_1 = geom1_copy.coords3d
|
|
79
|
+
geom2_copy = geom2.copy()
|
|
80
|
+
geom2_copy.standard_orientation()
|
|
81
|
+
coords3d_2 = geom2_copy.coords3d.copy()
|
|
82
|
+
|
|
83
|
+
# After bringing the Geometries into standard orientation we may
|
|
84
|
+
# still have to consider additional axis swaps and reflections to
|
|
85
|
+
# allow optimal atom matching using the Hungarian method.
|
|
86
|
+
|
|
87
|
+
# Six possible axis swaps, (3*2, (x, y, z)*((x), (y), (z))).
|
|
88
|
+
axes = (0, 1, 2)
|
|
89
|
+
swaps = list(it.permutations(axes))
|
|
90
|
+
# Eight possible reflections, (±x, ±y, ±z).
|
|
91
|
+
reflections = (
|
|
92
|
+
( 1, 1, 1), # no reflection
|
|
93
|
+
(-1, 1, 1), # reflect on yz plane
|
|
94
|
+
( 1, -1, 1), # reflect on xz plane
|
|
95
|
+
( 1, 1, -1), # reflect on xy plane
|
|
96
|
+
(-1, -1, 1), # reflect on yz and xz planes
|
|
97
|
+
(-1, 1, -1), # reflect on yz and xy planes
|
|
98
|
+
( 1, -1, -1), # reflect on xz and xy planes
|
|
99
|
+
(-1, -1, -1) # reflect on yz, xz, xy planes
|
|
100
|
+
)
|
|
101
|
+
# 48 combinations of six axis swaps and eight reflections.
|
|
102
|
+
transforms = list(it.product(swaps, reflections))
|
|
103
|
+
|
|
104
|
+
matched_rmsds = list()
|
|
105
|
+
matched_coords = list()
|
|
106
|
+
for i, transform in enumerate(transforms):
|
|
107
|
+
# Apply swap and reflection
|
|
108
|
+
c3d_trans = apply_transform(coords3d_2.copy(), *transform)
|
|
109
|
+
geom2_to_match = geom2.copy()
|
|
110
|
+
geom2_to_match.coords3d = c3d_trans
|
|
111
|
+
# Apply Hungarian method to the transformed Geometry
|
|
112
|
+
geom2_matched = match_geom_atoms(geom1_copy, geom2_to_match)
|
|
113
|
+
mrmsd = rmsd.kabsch_rmsd(coords3d_1, geom2_matched.coords3d)
|
|
114
|
+
matched_rmsds.append(mrmsd)
|
|
115
|
+
matched_coords.append(geom2_matched.coords)
|
|
116
|
+
|
|
117
|
+
# Break when the two geometries are similar. Then we don't have to
|
|
118
|
+
# apply the remaining transformations.
|
|
119
|
+
if mrmsd <= thresh:
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
matched_rmsds = np.array(matched_rmsds)
|
|
123
|
+
min_rmsd_ind = matched_rmsds.argmin()
|
|
124
|
+
min_rmsd = matched_rmsds.min()
|
|
125
|
+
best_matching_coords = matched_coords[min_rmsd_ind]
|
|
126
|
+
geom2_copy.coords = best_matching_coords
|
|
127
|
+
return min_rmsd, (geom1_copy, geom2_copy)
|
pysisyphus/testing.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
4
|
+
from pysisyphus.config import Config, DEFAULTS
|
|
5
|
+
from pysisyphus import logger
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import pytest
|
|
9
|
+
except ModuleNotFoundError:
|
|
10
|
+
logger.warning("pytest package could not be imported.")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
"""Inspired by
|
|
14
|
+
https://github.com/MolSSI/QCEngine/blob/master/qcengine/testing.py
|
|
15
|
+
|
|
16
|
+
Maybe implement something like this to make test access easier
|
|
17
|
+
https://stackoverflow.com/a/36601576/12486216
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_reason = "Calculator {} is not available."
|
|
22
|
+
_using_cache = dict()
|
|
23
|
+
# Python modules to be imported using importlib.import_module
|
|
24
|
+
IMPORT_DICT = {
|
|
25
|
+
"dftd4": "dftd4",
|
|
26
|
+
"pyscf": "pyscf",
|
|
27
|
+
"dalton": "daltonproject",
|
|
28
|
+
"qcengine": "qcengine",
|
|
29
|
+
"openmm": "simtk.openmm",
|
|
30
|
+
"thermoanalysis": "thermoanalysis",
|
|
31
|
+
"pyxtb": "xtb.interface",
|
|
32
|
+
"obabel": "openbabel.openbabel",
|
|
33
|
+
"geodesic": "geodesic_interpolate",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def module_available(calculator):
|
|
38
|
+
try:
|
|
39
|
+
import_name = IMPORT_DICT[calculator]
|
|
40
|
+
except KeyError:
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
_ = importlib.import_module(import_name)
|
|
45
|
+
available = True
|
|
46
|
+
except (ModuleNotFoundError, ImportError):
|
|
47
|
+
available = False
|
|
48
|
+
return available
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DummyMark:
|
|
52
|
+
def __init__(self, available):
|
|
53
|
+
self.args = (available,)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def using(calculator, set_pytest_mark=True):
|
|
57
|
+
"""Calling disabling set_pytest_mark avoids a runtime dependency on pytest."""
|
|
58
|
+
calculator = calculator.lower()
|
|
59
|
+
|
|
60
|
+
if calculator not in _using_cache:
|
|
61
|
+
available = False
|
|
62
|
+
|
|
63
|
+
# Look into .pysisyphusrc first
|
|
64
|
+
try:
|
|
65
|
+
cmd = Config[calculator]["cmd"]
|
|
66
|
+
except KeyError:
|
|
67
|
+
# Try defaults last
|
|
68
|
+
try:
|
|
69
|
+
cmd = DEFAULTS[calculator]
|
|
70
|
+
except KeyError:
|
|
71
|
+
cmd = None
|
|
72
|
+
|
|
73
|
+
# Look for dscf availability
|
|
74
|
+
if calculator == "turbomole":
|
|
75
|
+
cmd = "dscf"
|
|
76
|
+
|
|
77
|
+
# Check if cmd is available on $PATH
|
|
78
|
+
if cmd is not None:
|
|
79
|
+
available = bool(shutil.which(cmd))
|
|
80
|
+
# Handling native python packages from here
|
|
81
|
+
else:
|
|
82
|
+
available = module_available(calculator)
|
|
83
|
+
|
|
84
|
+
reason = _reason.format(calculator)
|
|
85
|
+
skipif = not available
|
|
86
|
+
if set_pytest_mark:
|
|
87
|
+
mark = pytest.mark.skipif(skipif, reason=reason)
|
|
88
|
+
else:
|
|
89
|
+
mark = DummyMark(skipif)
|
|
90
|
+
_using_cache[calculator] = mark
|
|
91
|
+
return _using_cache[calculator]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def available(calculator, **kwargs):
|
|
95
|
+
# True when skipif is False
|
|
96
|
+
return not using(calculator, **kwargs).args[0]
|
pysisyphus/thermo.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import h5py
|
|
2
|
+
import jinja2
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from thermoanalysis.QCData import QCData
|
|
6
|
+
from thermoanalysis.thermo import thermochemistry
|
|
7
|
+
|
|
8
|
+
can_thermoanalysis = True
|
|
9
|
+
except ModuleNotFoundError:
|
|
10
|
+
can_thermoanalysis = False
|
|
11
|
+
|
|
12
|
+
from pysisyphus.config import p_DEFAULT, T_DEFAULT
|
|
13
|
+
from pysisyphus.constants import AU2KJPERMOL, AU2KCALPERMOL
|
|
14
|
+
from pysisyphus.helpers_pure import highlight_text
|
|
15
|
+
from pysisyphus.Geometry import Geometry
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_thermoanalysis_from_hess_h5(
|
|
19
|
+
h5_fn, T=T_DEFAULT, p=p_DEFAULT, point_group="c1", return_geom=False
|
|
20
|
+
):
|
|
21
|
+
with h5py.File(h5_fn, "r") as handle:
|
|
22
|
+
masses = handle["masses"][:]
|
|
23
|
+
vibfreqs = handle["vibfreqs"][:]
|
|
24
|
+
coords3d = handle["coords3d"][:]
|
|
25
|
+
energy = handle.attrs["energy"]
|
|
26
|
+
mult = handle.attrs["mult"]
|
|
27
|
+
atoms = handle.attrs["atoms"]
|
|
28
|
+
|
|
29
|
+
thermo_dict = {
|
|
30
|
+
"masses": masses,
|
|
31
|
+
"wavenumbers": vibfreqs,
|
|
32
|
+
"coords3d": coords3d,
|
|
33
|
+
"scf_energy": energy,
|
|
34
|
+
"mult": mult,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
qcd = QCData(thermo_dict, point_group=point_group)
|
|
38
|
+
thermo = thermochemistry(qcd, temperature=T, pressure=p)
|
|
39
|
+
if return_geom:
|
|
40
|
+
geom = Geometry(atoms=atoms, coords=coords3d)
|
|
41
|
+
return thermo, geom
|
|
42
|
+
else:
|
|
43
|
+
return thermo
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
THERMO_TPL = jinja2.Template(
|
|
47
|
+
"""
|
|
48
|
+
{% if geom -%}
|
|
49
|
+
Geometry : {{ geom }}, {{ geom.atoms|length }} atoms
|
|
50
|
+
{%- endif %}
|
|
51
|
+
Temperature : {{ "%0.2f" % thermo.T }} K
|
|
52
|
+
Pressure : {{ thermo.p }} Pa
|
|
53
|
+
Total Mass : {{ "%0.4f" % thermo.M }} amu
|
|
54
|
+
|
|
55
|
+
! Symmetry is currently not supported in pysisyphus. !
|
|
56
|
+
! If not given otherwise, c1 and σ = 1 are assumed. !
|
|
57
|
+
Point Group : {{ thermo.point_group }}
|
|
58
|
+
Symmetry Number σ : {{ thermo.sym_num }}
|
|
59
|
+
Linear : {{ thermo.linear }}
|
|
60
|
+
|
|
61
|
+
+
|
|
62
|
+
| Normal Mode Wavenumbers
|
|
63
|
+
{{ sep }}
|
|
64
|
+
{% for nu in used_nus -%}
|
|
65
|
+
{{ "\t%04d" % loop.index }}: {{ nu }}
|
|
66
|
+
{% endfor -%}
|
|
67
|
+
{{ sep }}
|
|
68
|
+
{% if is_ts %}This should be a TS.{% endif %}
|
|
69
|
+
Expected {{ expected }} normal modes, got {{ used_nus|length}}.
|
|
70
|
+
|
|
71
|
+
+
|
|
72
|
+
| Inner energy U = U_el + U_vib + U_rot + U_trans
|
|
73
|
+
{{ sep }}
|
|
74
|
+
{{ fmt("U_el", thermo.U_el) }}
|
|
75
|
+
{{ fmt("ZPE", thermo.ZPE) }}
|
|
76
|
+
{{ fmt("U_vib (incl. ZPE)", thermo.U_vib) }}
|
|
77
|
+
{{ fmt("U_rot", thermo.U_rot) }}
|
|
78
|
+
{{ fmt("U_trans", thermo.U_trans) }}
|
|
79
|
+
{{ sep }}
|
|
80
|
+
{{ fmt("U", thermo.U_tot) }}
|
|
81
|
+
|
|
82
|
+
+
|
|
83
|
+
| Enthalpy H = U + kB*T
|
|
84
|
+
{{ sep }}
|
|
85
|
+
{{ fmt("U", thermo.U_tot) }}
|
|
86
|
+
{{ fmt("kB*T", thermo.kBT) }}
|
|
87
|
+
{{ sep }}
|
|
88
|
+
{{ fmt("H", thermo.H) }}
|
|
89
|
+
|
|
90
|
+
+
|
|
91
|
+
| Entropy correction T*S = T*(S_el + S_vib + S_rot + S_trans)
|
|
92
|
+
{{ sep }}
|
|
93
|
+
{{ fmt("T*S_el", thermo.TS_el) }}
|
|
94
|
+
{{ fmt("T*S_vib", thermo.TS_vib) }}
|
|
95
|
+
{{ fmt("T*S_rot", thermo.TS_rot) }}
|
|
96
|
+
{{ fmt("T*S_trans", thermo.TS_trans) }}
|
|
97
|
+
{{ sep }}
|
|
98
|
+
{{ fmt("T*S", thermo.TS_tot) }}
|
|
99
|
+
|
|
100
|
+
+
|
|
101
|
+
| Gibbs free energy G = H - T*S
|
|
102
|
+
{{ sep }}
|
|
103
|
+
{{ fmt("H", thermo.H) }}
|
|
104
|
+
{{ fmt("T*S", thermo.TS_tot) }}
|
|
105
|
+
{{ sep }}
|
|
106
|
+
{{ fmt("G", thermo.G) }}
|
|
107
|
+
{{ fmt("dG", thermo.dG) }}
|
|
108
|
+
""".strip()
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def print_thermoanalysis(
|
|
113
|
+
thermo, geom=None, is_ts=False, unit="joule", level=0, title=None
|
|
114
|
+
):
|
|
115
|
+
"""Print thermochemical analysis."""
|
|
116
|
+
|
|
117
|
+
units = {
|
|
118
|
+
"calorie": ("kcal mol⁻¹", AU2KCALPERMOL),
|
|
119
|
+
"joule": ("kJ mol⁻¹", AU2KJPERMOL),
|
|
120
|
+
}
|
|
121
|
+
unit_key, unit_conv = units[unit]
|
|
122
|
+
|
|
123
|
+
def fmt(key, hartree):
|
|
124
|
+
"""Output key & energy in Hartree and the chosen unit."""
|
|
125
|
+
kjm = hartree * unit_conv
|
|
126
|
+
return f"{key:<18}: {hartree: >16.8f} Eh ({kjm: >18.2f} {unit_key})"
|
|
127
|
+
|
|
128
|
+
# Separator
|
|
129
|
+
sep = "+-----------------------------------------------------------------+"
|
|
130
|
+
|
|
131
|
+
sub_modes = 5 if thermo.linear else 6
|
|
132
|
+
# Expect one real mode less if it is a TS
|
|
133
|
+
sub_modes += 1 if is_ts else 0
|
|
134
|
+
expected = 3 * thermo.atom_num - sub_modes
|
|
135
|
+
|
|
136
|
+
def fmt_nus(nus):
|
|
137
|
+
return [f"{nu: >8.2f} cm⁻¹" + (", excluded" if nu < 0.0 else "") for nu in nus]
|
|
138
|
+
|
|
139
|
+
rendered = THERMO_TPL.render(
|
|
140
|
+
geom=geom,
|
|
141
|
+
thermo=thermo,
|
|
142
|
+
org_nus=thermo.org_wavenumbers,
|
|
143
|
+
used_nus=fmt_nus(thermo.wavenumbers),
|
|
144
|
+
expected=expected,
|
|
145
|
+
is_ts=is_ts,
|
|
146
|
+
sep=sep,
|
|
147
|
+
fmt=fmt,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if title is None:
|
|
151
|
+
title = ""
|
|
152
|
+
else:
|
|
153
|
+
title = f", {title}"
|
|
154
|
+
print(highlight_text(f"Thermochemical corrections{title}", level=level))
|
|
155
|
+
print()
|
|
156
|
+
print(rendered)
|