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,63 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
class TableFormatter:
|
|
6
|
+
|
|
7
|
+
def __init__(self, header, fmts, min_width=7, space=3):
|
|
8
|
+
self.min_width = min_width + (space-1)
|
|
9
|
+
self.space = space
|
|
10
|
+
|
|
11
|
+
# Get lengths of header strings
|
|
12
|
+
widths = np.array([len(h) for h in header])
|
|
13
|
+
# Expand entries smaller than min_widht to min_width
|
|
14
|
+
smaller_indices = widths < min_width
|
|
15
|
+
widths[smaller_indices] = min_width
|
|
16
|
+
self.widths = widths
|
|
17
|
+
|
|
18
|
+
# Construct header
|
|
19
|
+
#header_fmts = ["{:" + "{}".format(width) + "s}"
|
|
20
|
+
# for width in self.widths]
|
|
21
|
+
header_fmts = self.min_width_fmts()
|
|
22
|
+
self._header = self.join_format(header_fmts, header)
|
|
23
|
+
self._header += "\n" + (self.space*" ").join(
|
|
24
|
+
["-"*width for width in self.widths]
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Modify fmts to consinder min_widths
|
|
28
|
+
self.fmts = self.min_width_fmts(fmts)
|
|
29
|
+
|
|
30
|
+
def min_width_fmts(self, raw_fmts=None):
|
|
31
|
+
if not raw_fmts:
|
|
32
|
+
raw_fmts = ["s" for _ in self.widths]
|
|
33
|
+
return ["{:>" + "{}".format(width) + fmt + "}"
|
|
34
|
+
for width, fmt in zip(self.widths, raw_fmts)]
|
|
35
|
+
|
|
36
|
+
def join_format(self, fmts, lst):
|
|
37
|
+
"""Format a given iterable lst with formats given in the iterable
|
|
38
|
+
lst and return the joined items of the formatted list."""
|
|
39
|
+
return (self.space*" ").join(
|
|
40
|
+
[fmt.format(item) for fmt, item in zip(fmts, lst)]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def header(self):
|
|
45
|
+
return self._header
|
|
46
|
+
|
|
47
|
+
def line(self, *args):
|
|
48
|
+
#formatted = " "["
|
|
49
|
+
return self.join_format(self.fmts, args)
|
|
50
|
+
|
|
51
|
+
def run():
|
|
52
|
+
header = "# |dx| |tangent|".split()
|
|
53
|
+
fmts = ["d", ".2E", ".3E"]
|
|
54
|
+
min_width = 10
|
|
55
|
+
tp = TableFormatter(header, fmts, min_width)
|
|
56
|
+
print(tp.header)
|
|
57
|
+
for i in range(10):
|
|
58
|
+
dx = random.random()
|
|
59
|
+
tangent = random.random()
|
|
60
|
+
print(tp.line(i, dx, tangent))
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
run()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import textwrap
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TablePrinter:
|
|
5
|
+
def __init__(
|
|
6
|
+
self,
|
|
7
|
+
header,
|
|
8
|
+
col_fmts,
|
|
9
|
+
width=12,
|
|
10
|
+
sub_underline=True,
|
|
11
|
+
shift_left=0,
|
|
12
|
+
fmts_update=None,
|
|
13
|
+
mark="*",
|
|
14
|
+
):
|
|
15
|
+
self.header = header
|
|
16
|
+
self.col_fmts = col_fmts
|
|
17
|
+
self.width = width
|
|
18
|
+
self.sub_underline = sub_underline
|
|
19
|
+
self.shift_left = shift_left
|
|
20
|
+
if fmts_update is None:
|
|
21
|
+
fmts_update = {}
|
|
22
|
+
self.fmts_update = fmts_update
|
|
23
|
+
self.mark = mark
|
|
24
|
+
|
|
25
|
+
w = str(self.width - 1)
|
|
26
|
+
|
|
27
|
+
self.fmts = {
|
|
28
|
+
"int": "{:>" + w + "d}",
|
|
29
|
+
"float": "{: >" + w + ".6f}",
|
|
30
|
+
"float_short": "{: >" + w + ".2f}",
|
|
31
|
+
"str": "{:>" + w + "s}",
|
|
32
|
+
"mark": "{:>1s}",
|
|
33
|
+
}
|
|
34
|
+
self.fmts.update(fmts_update)
|
|
35
|
+
if self.sub_underline:
|
|
36
|
+
self.header = [h.replace("_", " ") for h in self.header]
|
|
37
|
+
|
|
38
|
+
self.header_str = " ".join([h.rjust(self.width) for h in self.header])
|
|
39
|
+
mark_fmt = self.fmts["mark"]
|
|
40
|
+
self.conv_str = " ".join([self.fmts[fmt] + mark_fmt for fmt in self.col_fmts])
|
|
41
|
+
h0_len = len(self.header[0])
|
|
42
|
+
self.offset = self.width - h0_len
|
|
43
|
+
self.prefix = " " * (self.offset - self.shift_left)
|
|
44
|
+
self.sep = (
|
|
45
|
+
self.prefix
|
|
46
|
+
+ "-" * (len(self.header_str) - self.width + h0_len)
|
|
47
|
+
+ "-" * abs(self.shift_left)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def print_sep(self):
|
|
51
|
+
print(self.sep)
|
|
52
|
+
|
|
53
|
+
def print_header(self, with_sep=True):
|
|
54
|
+
print(self.header_str)
|
|
55
|
+
if with_sep:
|
|
56
|
+
self.print_sep()
|
|
57
|
+
|
|
58
|
+
def print_row(self, args, marks=None):
|
|
59
|
+
if marks is None:
|
|
60
|
+
marks = ["" for _ in args]
|
|
61
|
+
marked_args = list()
|
|
62
|
+
for arg, to_mark in zip(args, marks):
|
|
63
|
+
marked_args.append(arg)
|
|
64
|
+
marked_args.append(self.mark if to_mark else " ")
|
|
65
|
+
print(self.conv_str.format(*marked_args))
|
|
66
|
+
|
|
67
|
+
def print(self, *args, **kwargs):
|
|
68
|
+
text = " ".join([str(a) for a in args])
|
|
69
|
+
try:
|
|
70
|
+
level = kwargs["level"]
|
|
71
|
+
except KeyError:
|
|
72
|
+
level = 0
|
|
73
|
+
level_prefix = " " * level
|
|
74
|
+
print(textwrap.indent(text, self.prefix + level_prefix))
|
pysisyphus/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger("pysisyphus")
|
|
5
|
+
logger.setLevel(logging.DEBUG)
|
|
6
|
+
|
|
7
|
+
file_handler = logging.FileHandler("pysisyphus.log", mode="w", delay=True)
|
|
8
|
+
logger.addHandler(file_handler)
|
|
9
|
+
|
|
10
|
+
stdout_handler = logging.StreamHandler(sys.stdout)
|
|
11
|
+
stdout_handler.setLevel(logging.INFO)
|
|
12
|
+
logger.addHandler(stdout_handler)
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
# [1] https://pubs.acs.org/doi/pdf/10.1021/ct200290m?rand=dcfwsf09
|
|
2
|
+
# [2] https://onlinelibrary.wiley.com/doi/epdf/10.1002/jcc.23481
|
|
3
|
+
# [3] https://onlinelibrary.wiley.com/doi/epdf/10.1002/tcr.201600043
|
|
4
|
+
|
|
5
|
+
import itertools as it
|
|
6
|
+
from math import isclose
|
|
7
|
+
from typing import List
|
|
8
|
+
|
|
9
|
+
import autograd
|
|
10
|
+
import autograd.numpy as anp
|
|
11
|
+
import numpy as np
|
|
12
|
+
import numpy.typing as npt
|
|
13
|
+
|
|
14
|
+
from pysisyphus.calculators.Calculator import Calculator
|
|
15
|
+
from pysisyphus.elem_data import COVALENT_RADII
|
|
16
|
+
from pysisyphus.Geometry import Geometry
|
|
17
|
+
from pysisyphus.helpers import complete_fragments
|
|
18
|
+
from pysisyphus.helpers_pure import log
|
|
19
|
+
from pysisyphus.io.hdf5 import get_h5_group, resize_h5_group
|
|
20
|
+
from pysisyphus.linalg import finite_difference_hessian
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_data_model(atoms, max_cycles):
|
|
24
|
+
coord_size = 3 * len(atoms)
|
|
25
|
+
_1d = (max_cycles,)
|
|
26
|
+
_2d = (max_cycles, coord_size)
|
|
27
|
+
_3d = (max_cycles, coord_size, coord_size)
|
|
28
|
+
|
|
29
|
+
data_model = {
|
|
30
|
+
"cart_coords": _2d,
|
|
31
|
+
"energy": _1d,
|
|
32
|
+
"forces": _2d,
|
|
33
|
+
"hessian": _3d,
|
|
34
|
+
"true_energy": _1d,
|
|
35
|
+
"true_forces": _2d,
|
|
36
|
+
"true_hessian": _3d,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return data_model
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class CovRadiiSumZero(Exception):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def afir_closure(
|
|
47
|
+
fragment_indices, cov_radii, gamma, rho=1, p=6, prefactor=1.0, logger=None
|
|
48
|
+
):
|
|
49
|
+
"""rho=1 pushes fragments together, rho=-1 pulls fragments apart."""
|
|
50
|
+
|
|
51
|
+
# See https://onlinelibrary.wiley.com/doi/full/10.1002/qua.24757
|
|
52
|
+
# Eq. (9) for extension to 3 fragments.
|
|
53
|
+
assert len(fragment_indices) == 2
|
|
54
|
+
|
|
55
|
+
inds = np.array(list(it.product(*fragment_indices)))
|
|
56
|
+
cov_rad_sums = cov_radii[inds].sum(axis=1)
|
|
57
|
+
|
|
58
|
+
if isclose(cov_rad_sums.sum(), 0.0, abs_tol=1e-10):
|
|
59
|
+
raise CovRadiiSumZero("Sum of covalent radii is 0.0!")
|
|
60
|
+
|
|
61
|
+
# 3.8164 Angstrom in Bohr
|
|
62
|
+
R0 = 7.21195
|
|
63
|
+
# 1.0061 kJ/mol to Hartree
|
|
64
|
+
epsilon = 0.000383203368
|
|
65
|
+
|
|
66
|
+
# Avoid division by zero for gamma = 0.
|
|
67
|
+
if gamma == 0.0:
|
|
68
|
+
alpha = 0.0
|
|
69
|
+
else:
|
|
70
|
+
alpha = gamma / (
|
|
71
|
+
(2 ** (-1 / 6) - (1 + (1 + gamma / epsilon) ** 0.5) ** (-1 / 6)) * R0
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
rho_verbose = {1: ("pushing", "together"), -1: ("pulling", "apart")}
|
|
75
|
+
w1, w2 = rho_verbose[rho]
|
|
76
|
+
log(
|
|
77
|
+
logger,
|
|
78
|
+
f"Creating AFIR closure with α={alpha:.6f}, prefactor {prefactor:.6f}, "
|
|
79
|
+
f"rho={rho}, {w1} framgents {w2}",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def afir_func(coords3d):
|
|
83
|
+
diffs = anp.diff(coords3d[inds], axis=1).reshape(-1, 3)
|
|
84
|
+
rs = anp.linalg.norm(diffs, axis=1)
|
|
85
|
+
|
|
86
|
+
omegas = (cov_rad_sums / rs) ** p
|
|
87
|
+
|
|
88
|
+
f = prefactor * alpha * rho * (omegas * rs).sum() / omegas.sum()
|
|
89
|
+
return f
|
|
90
|
+
|
|
91
|
+
return afir_func
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class AFIR(Calculator):
|
|
95
|
+
def __init__(
|
|
96
|
+
self,
|
|
97
|
+
calculator: Calculator,
|
|
98
|
+
fragment_indices: List[List[int]],
|
|
99
|
+
gamma: npt.ArrayLike,
|
|
100
|
+
rho: npt.ArrayLike = 1,
|
|
101
|
+
p: int = 6,
|
|
102
|
+
ignore_hydrogen: bool = False,
|
|
103
|
+
zero_hydrogen: bool = True,
|
|
104
|
+
complete_fragments: bool = True,
|
|
105
|
+
dump: bool = True,
|
|
106
|
+
h5_fn: str = "afir.h5",
|
|
107
|
+
h5_group_name: str = "afir",
|
|
108
|
+
**kwargs,
|
|
109
|
+
):
|
|
110
|
+
"""
|
|
111
|
+
Artifical Force Induced Reaction calculator.
|
|
112
|
+
|
|
113
|
+
Currently, there are no automated drivers to run large-scale AFIR calculations
|
|
114
|
+
with many different initial orientations and/or increasing collision energy
|
|
115
|
+
parameter γ. Nontheless, selected AFIR calculations can be carried out by hand.
|
|
116
|
+
After convergence, artificial potential & forces, as well as real energies
|
|
117
|
+
and forces can be plotted with 'pysisplot --afir'. The highest energy point
|
|
118
|
+
along the AFIR path can then be selected for a subsequent TS-optimization,
|
|
119
|
+
e.g. via 'pysistrj --get [index] optimzation_trj.xyz'.
|
|
120
|
+
|
|
121
|
+
Future versions of pysisyphus may provide drivers for more automatted
|
|
122
|
+
AFIR calculations.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
calculator
|
|
127
|
+
Actual QC calculator that provides energies and its derivatives,
|
|
128
|
+
that are modified by the AFIR calculator, e.g., ORCA or Psi4.
|
|
129
|
+
fragment_indices
|
|
130
|
+
List of lists of integers, specifying the separate fragments. If
|
|
131
|
+
the indices in theses lists don't comprise all atoms in the molecule,
|
|
132
|
+
the reamining indices will be added as a separate fragment. If a AFIR
|
|
133
|
+
calculation is carried out with 2 fragments and 'complete_fragments' is
|
|
134
|
+
True (see below) it is enough to specify only
|
|
135
|
+
the indices of one fragment, e.g., for a system of 10 atoms
|
|
136
|
+
'fragment_indices=[[0,1,2,3]]' is enough. The second system will be set
|
|
137
|
+
up automatically with indices [4,5,6,7,8,9].
|
|
138
|
+
gamma
|
|
139
|
+
Collision energy parameter γ in au. For 2 fragments it can be a single
|
|
140
|
+
integer, while for > 2 fragments a list of gammas must be given, specifying
|
|
141
|
+
the pair-wise collision energy parameters. For 3 fragments 3 gammas must be
|
|
142
|
+
given [γ_01, γ_02, γ_12], for 4 fragments 6 gammas would be required
|
|
143
|
+
[γ_01, γ_02, γ_03, γ_12, γ_13, γ_23] and so on.
|
|
144
|
+
rho
|
|
145
|
+
Direction of the artificial force, either 1 or -1. The same comments
|
|
146
|
+
as for gamma apply. For 2 fragments a single integer is enough, for
|
|
147
|
+
> 2 fragments a list of rhos must be given (see above). For rho=1
|
|
148
|
+
fragments are pushed together, for rho=-1 fragments are pulled apart.
|
|
149
|
+
p
|
|
150
|
+
Exponent p used in the calculation of the weight function ω. Defaults
|
|
151
|
+
to 6 and probably does not have to be changed.
|
|
152
|
+
ignore_hydrogen
|
|
153
|
+
Whether hydrogens are ignored in the calculation of the artificial force.
|
|
154
|
+
All weights between atom pairs containing hydrogen will be set to 0.
|
|
155
|
+
zero_hydrogen
|
|
156
|
+
Whether to use 0.0 as covalent radius for hydrogen in the weight function.
|
|
157
|
+
Compared to 'ignore_hydrogen', which results in zero weights for all atom
|
|
158
|
+
pairs involving hydrogen, 'zero_hydrogen' may be non-zero, depending on the
|
|
159
|
+
covalent radius of the second atom in the pair.
|
|
160
|
+
complete_fragments
|
|
161
|
+
Whether an incomplete specification in 'fragment_indices' is automatically
|
|
162
|
+
completed.
|
|
163
|
+
dump
|
|
164
|
+
Whether an HDF5 file is created.
|
|
165
|
+
h5_fn
|
|
166
|
+
Filename of the HDF5 file used for dumping.
|
|
167
|
+
h5_group_name
|
|
168
|
+
HDF5 group name used for dumping.
|
|
169
|
+
|
|
170
|
+
Other Parameters
|
|
171
|
+
----------------
|
|
172
|
+
**kwargs
|
|
173
|
+
Keyword arguments passed to the Calculator baseclass.
|
|
174
|
+
"""
|
|
175
|
+
super().__init__(**kwargs)
|
|
176
|
+
|
|
177
|
+
self.calculator = calculator
|
|
178
|
+
# Update own charge and multiplicity with values from the wrapped
|
|
179
|
+
# calculator.
|
|
180
|
+
self.charge = calculator.charge
|
|
181
|
+
self.mult = calculator.mult
|
|
182
|
+
self.fragment_indices = fragment_indices
|
|
183
|
+
assert len(self.fragment_indices) > 0
|
|
184
|
+
self.gamma = np.atleast_1d(gamma).astype(float)
|
|
185
|
+
self.rho = np.atleast_1d(rho).astype(int)
|
|
186
|
+
np.testing.assert_allclose(np.abs(self.rho), np.ones_like(self.rho))
|
|
187
|
+
self.p = p
|
|
188
|
+
self.ignore_hydrogen = ignore_hydrogen
|
|
189
|
+
self.zero_hydrogen = zero_hydrogen
|
|
190
|
+
self.complete_fragments = complete_fragments
|
|
191
|
+
self.dump = dump
|
|
192
|
+
self.h5_fn = self.out_dir / h5_fn
|
|
193
|
+
self.h5_group_name = h5_group_name
|
|
194
|
+
# We can't initialize the HDF5 group as we don't know the shape of
|
|
195
|
+
# atoms/coords yet. So we wait until after the first calculation.
|
|
196
|
+
self.h5_cycles = 50
|
|
197
|
+
|
|
198
|
+
if self.ignore_hydrogen:
|
|
199
|
+
self.log("No artificial force contribution from hydrogens!")
|
|
200
|
+
self.atoms = None
|
|
201
|
+
self.calc_counter = 0
|
|
202
|
+
self.h5_group_initialized = False
|
|
203
|
+
|
|
204
|
+
"""We try to keep charge and multiplicity consistent between the AFIR
|
|
205
|
+
calculator and the actual wrapped calculator. But we will always return
|
|
206
|
+
the charge and multiplicity of the underlying calculator."""
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def charge(self):
|
|
210
|
+
return self.calculator.charge
|
|
211
|
+
|
|
212
|
+
@charge.setter
|
|
213
|
+
def charge(self, charge):
|
|
214
|
+
try:
|
|
215
|
+
self.calculator.charge = charge
|
|
216
|
+
except AttributeError:
|
|
217
|
+
pass
|
|
218
|
+
self._charge = charge
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def mult(self):
|
|
222
|
+
return self.calculator.mult
|
|
223
|
+
|
|
224
|
+
@mult.setter
|
|
225
|
+
def mult(self, mult):
|
|
226
|
+
try:
|
|
227
|
+
self.calculator.mult = mult
|
|
228
|
+
except AttributeError:
|
|
229
|
+
pass
|
|
230
|
+
self._mult = mult
|
|
231
|
+
|
|
232
|
+
def init_h5_group(self, atoms, max_cycles=None):
|
|
233
|
+
if max_cycles is None:
|
|
234
|
+
max_cycles = self.h5_cycles
|
|
235
|
+
self.data_model = get_data_model(atoms, max_cycles)
|
|
236
|
+
# h5_group = get_h5_group(
|
|
237
|
+
# self.h5_fn, self.h5_group_name, self.data_model, reset=True
|
|
238
|
+
# )
|
|
239
|
+
self.h5_group_initialized = True
|
|
240
|
+
# return h5_group
|
|
241
|
+
|
|
242
|
+
def dump_h5(self, atoms, coords, results):
|
|
243
|
+
# Initialize if not done yet
|
|
244
|
+
# if not self.h5_group_initialized:
|
|
245
|
+
# h5_group = self.init_h5_group(atoms)
|
|
246
|
+
# # Write atoms only once
|
|
247
|
+
# h5_group.attrs["atoms"] = atoms
|
|
248
|
+
|
|
249
|
+
# h5_group = get_h5_group(self.h5_fn, self.h5_group_name)
|
|
250
|
+
|
|
251
|
+
# # Check if HDF5 datasets have to be resized
|
|
252
|
+
# cur_max_cycles = h5_group["cart_coords"].shape[0]
|
|
253
|
+
# need_resize = self.calc_counter > cur_max_cycles - 5
|
|
254
|
+
# if need_resize:
|
|
255
|
+
# new_max_cycles = cur_max_cycles + self.h5_cycles
|
|
256
|
+
# resize_h5_group(h5_group, max_cycles=new_max_cycles)
|
|
257
|
+
|
|
258
|
+
# for k, v in results.items():
|
|
259
|
+
# h5_group[k][self.calc_counter] = v
|
|
260
|
+
# h5_group["cart_coords"][self.calc_counter] = coords
|
|
261
|
+
# h5_group.attrs["cur_cycle"] = self.calc_counter
|
|
262
|
+
# h5_group.file.close()
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
def log_fragments(self):
|
|
266
|
+
self.log(f"Using {len(self.fragment_indices)} fragments")
|
|
267
|
+
for i, frag in enumerate(self.fragment_indices):
|
|
268
|
+
self.log(f"Fragment {i:02d}, {len(frag)} atoms:")
|
|
269
|
+
self.log(f"\t{frag}")
|
|
270
|
+
|
|
271
|
+
def write_fragment_geoms(self, atoms, coords):
|
|
272
|
+
geom = Geometry(atoms, coords)
|
|
273
|
+
for i, frag in enumerate(self.fragment_indices):
|
|
274
|
+
frag_geom = geom.get_subgeom(frag)
|
|
275
|
+
fn = self.make_fn(f"frag_geom_{i:02d}.xyz")
|
|
276
|
+
with open(fn, "w") as handle:
|
|
277
|
+
handle.write(frag_geom.as_xyz())
|
|
278
|
+
self.log(f"Wrote geometry of fragment {i:02d} to {fn}.")
|
|
279
|
+
|
|
280
|
+
def set_atoms_and_funcs(self, atoms, coords):
|
|
281
|
+
"""Initially atoms was also an argument to the constructor of AFIR.
|
|
282
|
+
I removed it so creation becomes easier.
|
|
283
|
+
The first time a calculation is requested with a proper atom set
|
|
284
|
+
everything is set up (cov. radii, afir function and corresponding
|
|
285
|
+
gradient). Afterwards there is only a check if atoms != None and it
|
|
286
|
+
is expected that all functions are properly set.
|
|
287
|
+
|
|
288
|
+
fragment_indices can also be incomplete w.r.t. to the number of
|
|
289
|
+
atoms. If the sum of the specified fragment atoms is less than the
|
|
290
|
+
number of atoms present then all remaining unspecified atoms will
|
|
291
|
+
be gathered in one fragment.
|
|
292
|
+
"""
|
|
293
|
+
|
|
294
|
+
if self.atoms is not None:
|
|
295
|
+
assert self.atoms == atoms
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
self.log("Setting atoms on AFIR calculator")
|
|
299
|
+
self.atoms = atoms
|
|
300
|
+
if self.complete_fragments:
|
|
301
|
+
self.fragment_indices = complete_fragments(
|
|
302
|
+
self.atoms, self.fragment_indices
|
|
303
|
+
)
|
|
304
|
+
self.log_fragments()
|
|
305
|
+
self.write_fragment_geoms(atoms, coords)
|
|
306
|
+
|
|
307
|
+
hydrogen_inds = [i for i, atom in enumerate(atoms) if atom.lower() == "h"]
|
|
308
|
+
if self.ignore_hydrogen:
|
|
309
|
+
fragment_indices = list()
|
|
310
|
+
for i, frag_inds in enumerate(self.fragment_indices):
|
|
311
|
+
frag_inds_no_h = [j for j in frag_inds if j not in hydrogen_inds]
|
|
312
|
+
fragment_indices.append(frag_inds_no_h)
|
|
313
|
+
dropped_hydrogens = len(frag_inds) - len(frag_inds_no_h)
|
|
314
|
+
self.log(f"Ignoring {dropped_hydrogens} hydrogen(s) from fragment {i}.")
|
|
315
|
+
self.fragment_indices = fragment_indices
|
|
316
|
+
|
|
317
|
+
self.cov_radii_org = np.array([COVALENT_RADII[atom.lower()] for atom in atoms])
|
|
318
|
+
self.cov_radii = self.cov_radii_org.copy()
|
|
319
|
+
if self.zero_hydrogen:
|
|
320
|
+
self.cov_radii[hydrogen_inds] = 0.0
|
|
321
|
+
self.log("Set covalent radii")
|
|
322
|
+
self.afir_funcs = list()
|
|
323
|
+
self.afir_grad_funcs = list()
|
|
324
|
+
pairs = list(it.combinations(self.fragment_indices, 2))
|
|
325
|
+
prefactor = 1 / len(pairs)
|
|
326
|
+
self.log(
|
|
327
|
+
f"Doing AFIR with {len(pairs)} fragment pairs and prefactor={prefactor:.4f}"
|
|
328
|
+
)
|
|
329
|
+
try:
|
|
330
|
+
self.gamma = [float(self.gamma)] * len(pairs)
|
|
331
|
+
except TypeError:
|
|
332
|
+
assert len(self.gamma) == len(pairs)
|
|
333
|
+
try:
|
|
334
|
+
self.rho = [float(self.rho)] * len(pairs)
|
|
335
|
+
except TypeError:
|
|
336
|
+
assert len(self.rho) == len(pairs)
|
|
337
|
+
self.log(f"Using gamma(s): {self.gamma}")
|
|
338
|
+
self.log(f" Using rho(s): {self.rho}")
|
|
339
|
+
|
|
340
|
+
for (frag1, frag2), gamma, rho in zip(pairs, self.gamma, self.rho):
|
|
341
|
+
afir_func = afir_closure(
|
|
342
|
+
(frag1, frag2),
|
|
343
|
+
self.cov_radii,
|
|
344
|
+
gamma=gamma,
|
|
345
|
+
rho=rho,
|
|
346
|
+
p=self.p,
|
|
347
|
+
prefactor=prefactor,
|
|
348
|
+
logger=self.logger,
|
|
349
|
+
)
|
|
350
|
+
afir_grad_func = autograd.grad(afir_func)
|
|
351
|
+
self.afir_funcs.append(afir_func)
|
|
352
|
+
self.afir_grad_funcs.append(afir_grad_func)
|
|
353
|
+
self.log("Created and set AFIR function & gradient function.")
|
|
354
|
+
|
|
355
|
+
def get_energy(self, atoms, coords, **prepare_kwargs):
|
|
356
|
+
self.set_atoms_and_funcs(atoms, coords)
|
|
357
|
+
|
|
358
|
+
true_results = self.calculator.get_energy(atoms, coords, **prepare_kwargs)
|
|
359
|
+
true_energy = true_results["energy"]
|
|
360
|
+
coords3d = coords.reshape(-1, 3)
|
|
361
|
+
# Iterate over all fragment pairs
|
|
362
|
+
afir_energy = sum([afir_func(coords3d) for afir_func in self.afir_funcs])
|
|
363
|
+
self.log()
|
|
364
|
+
|
|
365
|
+
results = {
|
|
366
|
+
"energy": true_energy + afir_energy,
|
|
367
|
+
"true_energy": true_energy,
|
|
368
|
+
}
|
|
369
|
+
if self.dump:
|
|
370
|
+
self.dump_h5(atoms, coords, results)
|
|
371
|
+
self.calc_counter += 1
|
|
372
|
+
return results
|
|
373
|
+
|
|
374
|
+
def get_forces(self, atoms, coords, **prepare_kwargs):
|
|
375
|
+
self.set_atoms_and_funcs(atoms, coords)
|
|
376
|
+
|
|
377
|
+
true_results = self.calculator.get_forces(atoms, coords, **prepare_kwargs)
|
|
378
|
+
true_energy = true_results["energy"]
|
|
379
|
+
true_forces = true_results["forces"]
|
|
380
|
+
|
|
381
|
+
coords3d = coords.reshape(-1, 3)
|
|
382
|
+
afir_energy = 0.0
|
|
383
|
+
afir_forces = np.zeros_like(coords)
|
|
384
|
+
# Iterate over all fragment pairs
|
|
385
|
+
for afir_func, afir_grad_func in zip(self.afir_funcs, self.afir_grad_funcs):
|
|
386
|
+
afir_energy += afir_func(coords3d)
|
|
387
|
+
# Add negative of the gradient (the force)
|
|
388
|
+
afir_forces += -afir_grad_func(coords3d).flatten()
|
|
389
|
+
|
|
390
|
+
true_norm = np.linalg.norm(true_forces)
|
|
391
|
+
afir_norm = np.linalg.norm(afir_forces)
|
|
392
|
+
self.log(
|
|
393
|
+
f"\ntrue_energy={true_energy:.6f} au\n"
|
|
394
|
+
f"afir_energy={afir_energy:.6f} au\n"
|
|
395
|
+
f" sum_energy={true_energy+afir_energy:.6f} au\n"
|
|
396
|
+
f"norm(true_forces)={true_norm:.6f} au/bohr\n"
|
|
397
|
+
f"norm(afir_forces)={afir_norm:.6f} au/bohr\n"
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
results = {
|
|
401
|
+
"energy": true_energy + afir_energy,
|
|
402
|
+
"forces": true_forces + afir_forces,
|
|
403
|
+
"true_forces": true_forces,
|
|
404
|
+
"true_energy": true_energy,
|
|
405
|
+
}
|
|
406
|
+
if self.dump:
|
|
407
|
+
self.dump_h5(atoms, coords, results)
|
|
408
|
+
self.calc_counter += 1
|
|
409
|
+
return results
|
|
410
|
+
|
|
411
|
+
def afir_fd_hessian_wrapper(self, coords3d, afir_grad_func):
|
|
412
|
+
coords = coords3d.flatten()
|
|
413
|
+
|
|
414
|
+
def grad_func(coords):
|
|
415
|
+
afir_grad = afir_grad_func(coords.reshape(-1, 3))
|
|
416
|
+
return afir_grad.flatten()
|
|
417
|
+
|
|
418
|
+
fd_hessian = finite_difference_hessian(coords, grad_func, acc=4)
|
|
419
|
+
return fd_hessian
|
|
420
|
+
|
|
421
|
+
def get_hessian(self, atoms, coords, **prepare_kwargs):
|
|
422
|
+
self.set_atoms_and_funcs(atoms, coords)
|
|
423
|
+
|
|
424
|
+
true_results = self.calculator.get_hessian(atoms, coords, **prepare_kwargs)
|
|
425
|
+
true_energy = true_results["energy"]
|
|
426
|
+
true_hessian = true_results["hessian"]
|
|
427
|
+
|
|
428
|
+
coords3d = coords.reshape(-1, 3)
|
|
429
|
+
afir_energy = 0.0
|
|
430
|
+
afir_hessian = np.zeros_like(true_hessian)
|
|
431
|
+
# Iterate over all fragment pairs
|
|
432
|
+
for afir_func, afir_grad_func in zip(self.afir_funcs, self.afir_grad_funcs):
|
|
433
|
+
afir_energy += afir_func(coords3d)
|
|
434
|
+
# AFIR Hessian from finite differences
|
|
435
|
+
afir_hessian += self.afir_fd_hessian_wrapper(coords3d, afir_grad_func)
|
|
436
|
+
|
|
437
|
+
results = {
|
|
438
|
+
"energy": true_energy + afir_energy,
|
|
439
|
+
"hessian": true_hessian + afir_hessian,
|
|
440
|
+
"true_hessian": true_hessian,
|
|
441
|
+
"true_energy": true_energy,
|
|
442
|
+
}
|
|
443
|
+
if self.dump:
|
|
444
|
+
self.dump_h5(atoms, coords, results)
|
|
445
|
+
self.calc_counter += 1
|
|
446
|
+
return results
|
|
447
|
+
|
|
448
|
+
def __repr__(self):
|
|
449
|
+
return self.__str__()
|
|
450
|
+
|
|
451
|
+
def __str__(self):
|
|
452
|
+
return f"AFIR({self.calculator})"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pysisyphus.calculators.AnaPotBase import AnaPotBase
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AnaPot(AnaPotBase):
|
|
7
|
+
def __init__(self, **kwargs):
|
|
8
|
+
kwargs_ = {
|
|
9
|
+
"V_str": "4 + 4.5*x - 4*y + x**2 + 2*y**2-2*x*y + x**4 - 2*x**2*y",
|
|
10
|
+
"xlim": (-2, 2.5),
|
|
11
|
+
"ylim": (0, 5),
|
|
12
|
+
"levels": np.linspace(-3, 4, 80),
|
|
13
|
+
"minima": ((-1.05274, 1.02776, 0), (1.94101, 3.85427, 0)),
|
|
14
|
+
"saddles": ((0.6117313, 1.4929732, 0.0),),
|
|
15
|
+
}
|
|
16
|
+
kwargs_.update(kwargs)
|
|
17
|
+
super().__init__(**kwargs_)
|
|
18
|
+
|
|
19
|
+
def __str__(self):
|
|
20
|
+
return "AnaPot calculator"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sympy as sym
|
|
3
|
+
from sympy import atan, exp, tan, sin, pi
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from pysisyphus.calculators.AnaPotBase import AnaPotBase
|
|
7
|
+
from pysisyphus.calculators.Calculator import Calculator
|
|
8
|
+
|
|
9
|
+
# https://www.wolframalpha.com/input/?i=plot+arccot(-exp(y)*cot(x/2-pi/4))+-+2*exp(-(y-sin(x))^2/2)
|
|
10
|
+
# [1] http://aip.scitation.org/doi/abs/10.1063/1.461606
|
|
11
|
+
# https://www.wolframalpha.com/input/?i=derivative+of+(arccot(-exp(y)*cot(x/2-pi/4))+-+2*exp(-(y-sin(x))^2/2))
|
|
12
|
+
|
|
13
|
+
class AnaPot2_(Calculator):
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
super().__init__()
|
|
17
|
+
|
|
18
|
+
def get_energy(self, atoms, coords):
|
|
19
|
+
x, y, z = coords
|
|
20
|
+
cot = 1 / np.tan(x/2 - np.pi/4)
|
|
21
|
+
arccot = np.arctan(
|
|
22
|
+
1 / (-np.exp(y) * cot)
|
|
23
|
+
)
|
|
24
|
+
energy = (
|
|
25
|
+
arccot - 2 * np.exp(-(y-np.sin(x))**2 / 2)
|
|
26
|
+
)
|
|
27
|
+
return {"energy": energy}
|
|
28
|
+
|
|
29
|
+
def __str__(self):
|
|
30
|
+
return "AnaPot2 calculator"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AnaPot2(AnaPotBase):
|
|
34
|
+
"""We can't use sympify as it replaces 1/tan by cot and this isn't
|
|
35
|
+
supported by numpy when we call lambdify."""
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
x, y = sym.symbols("x y")
|
|
39
|
+
V_str = atan(exp(-y)/tan(x/2 + pi/4)) - 2*exp(-(y - sin(x))**2/2)
|
|
40
|
+
# xlim = (-np.pi/2, np.pi)
|
|
41
|
+
xlim = (-np.pi, np.pi)
|
|
42
|
+
ylim = (-2, 2)
|
|
43
|
+
levels = np.linspace(-2, 1, 40)
|
|
44
|
+
super().__init__(V_str=V_str, xlim=xlim, ylim=ylim, levels=levels,
|
|
45
|
+
use_sympify=False)
|
|
46
|
+
|
|
47
|
+
def __str__(self):
|
|
48
|
+
return "AnaPot2 calculator"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from pysisyphus.calculators.AnaPotBase import AnaPotBase
|
|
2
|
+
|
|
3
|
+
# [1] http://www.cims.nyu.edu/~eve2/string_jcp_simplified07.pdf
|
|
4
|
+
|
|
5
|
+
class AnaPot3(AnaPotBase):
|
|
6
|
+
|
|
7
|
+
def __init__(self):
|
|
8
|
+
V_str = "(1 - x**2 - y**2)**2 + (y**2) / (x**2 + y**2)"
|
|
9
|
+
super(AnaPot3, self).__init__(V_str=V_str)
|
|
10
|
+
|
|
11
|
+
def __str__(self):
|
|
12
|
+
return "AnaPot3 calculator"
|