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,140 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from ase.calculators.calculator import Calculator
|
|
5
|
+
from ase.constraints import FixAtoms, FixBondLengths
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HarmonicFixAtoms(Calculator):
|
|
9
|
+
"""
|
|
10
|
+
Harmonic position restraint on a subset of atoms.
|
|
11
|
+
|
|
12
|
+
E = 1/2 * k_fix * sum_i |r_i - r_i^ref|^2
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
implemented_properties = ["energy", "forces"]
|
|
16
|
+
|
|
17
|
+
def __init__(self, indices, ref_positions, k_fix=300.0):
|
|
18
|
+
super().__init__()
|
|
19
|
+
idx = np.asarray(indices, dtype=int).ravel()
|
|
20
|
+
if idx.size == 0:
|
|
21
|
+
raise ValueError("HarmonicFixAtoms requires at least one index.")
|
|
22
|
+
ref_pos = np.asarray(ref_positions, dtype=float)
|
|
23
|
+
if ref_pos.shape != (idx.size, 3):
|
|
24
|
+
raise ValueError(
|
|
25
|
+
f"ref_positions must have shape ({idx.size}, 3), got {ref_pos.shape}"
|
|
26
|
+
)
|
|
27
|
+
self.indices = idx
|
|
28
|
+
self.ref_positions = ref_pos
|
|
29
|
+
self.k_fix = float(k_fix)
|
|
30
|
+
|
|
31
|
+
def calculate(self, atoms, properties, system_changes):
|
|
32
|
+
super().calculate(atoms, properties, system_changes)
|
|
33
|
+
pos = atoms.get_positions().astype(float)
|
|
34
|
+
disp = pos[self.indices] - self.ref_positions
|
|
35
|
+
energy = 0.5 * self.k_fix * np.sum(disp ** 2)
|
|
36
|
+
forces = np.zeros_like(pos, dtype=float)
|
|
37
|
+
forces[self.indices] = -self.k_fix * disp
|
|
38
|
+
self.results = {
|
|
39
|
+
"energy": float(energy),
|
|
40
|
+
"forces": forces,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class HarmonicFixBondLengths(Calculator):
|
|
45
|
+
"""
|
|
46
|
+
Harmonic bond-length restraint for selected atom pairs.
|
|
47
|
+
|
|
48
|
+
E = 1/2 * k_bond * sum_ij (d_ij - d_ij^ref)^2
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
implemented_properties = ["energy", "forces"]
|
|
52
|
+
|
|
53
|
+
def __init__(self, pairs, ref_distances, k_bond=300.0):
|
|
54
|
+
super().__init__()
|
|
55
|
+
pairs = np.asarray(pairs, dtype=int)
|
|
56
|
+
if pairs.ndim != 2 or pairs.shape[1] != 2:
|
|
57
|
+
raise ValueError("pairs must have shape (n_pairs, 2)")
|
|
58
|
+
ref_distances = np.asarray(ref_distances, dtype=float).ravel()
|
|
59
|
+
if ref_distances.shape[0] != pairs.shape[0]:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
"ref_distances must have same length as pairs"
|
|
62
|
+
)
|
|
63
|
+
self.pairs = pairs
|
|
64
|
+
self.ref_distances = ref_distances
|
|
65
|
+
self.k_bond = float(k_bond)
|
|
66
|
+
|
|
67
|
+
def calculate(self, atoms, properties, system_changes):
|
|
68
|
+
super().calculate(atoms, properties, system_changes)
|
|
69
|
+
pos = atoms.get_positions().astype(float)
|
|
70
|
+
i_idx = self.pairs[:, 0]
|
|
71
|
+
j_idx = self.pairs[:, 1]
|
|
72
|
+
rij = pos[i_idx] - pos[j_idx]
|
|
73
|
+
dist = np.linalg.norm(rij, axis=1)
|
|
74
|
+
diff = dist - self.ref_distances
|
|
75
|
+
energy = 0.5 * self.k_bond * np.sum(diff ** 2)
|
|
76
|
+
inv_dist = np.zeros_like(dist)
|
|
77
|
+
nonzero = dist > 0.0
|
|
78
|
+
inv_dist[nonzero] = 1.0 / dist[nonzero]
|
|
79
|
+
force_scale = -self.k_bond * diff * inv_dist
|
|
80
|
+
fij = (force_scale[:, None] * rij).astype(float)
|
|
81
|
+
forces = np.zeros_like(pos, dtype=float)
|
|
82
|
+
np.add.at(forces, i_idx, fij)
|
|
83
|
+
np.add.at(forces, j_idx, -fij)
|
|
84
|
+
self.results = {
|
|
85
|
+
"energy": float(energy),
|
|
86
|
+
"forces": forces,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _iter_constraints(atoms):
|
|
91
|
+
constraints = getattr(atoms, "constraints", None)
|
|
92
|
+
if constraints is None:
|
|
93
|
+
return []
|
|
94
|
+
if isinstance(constraints, (list, tuple)):
|
|
95
|
+
return list(constraints)
|
|
96
|
+
return [constraints]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def build_harmonic_constraint_calculators(atoms, k_fix=300.0, k_bond=300.0):
|
|
100
|
+
"""
|
|
101
|
+
Build harmonic-restraint calculators from ASE constraints.
|
|
102
|
+
|
|
103
|
+
Supported constraints:
|
|
104
|
+
- FixAtoms
|
|
105
|
+
- FixBondLength / FixBondLengths
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
calculators = []
|
|
109
|
+
constraints = _iter_constraints(atoms)
|
|
110
|
+
remaining_constraints = []
|
|
111
|
+
for constraint in constraints:
|
|
112
|
+
if isinstance(constraint, FixAtoms):
|
|
113
|
+
indices = np.asarray(constraint.index, dtype=int)
|
|
114
|
+
ref_positions = atoms.get_positions()[indices]
|
|
115
|
+
calculators.append(
|
|
116
|
+
HarmonicFixAtoms(indices, ref_positions, k_fix=k_fix)
|
|
117
|
+
)
|
|
118
|
+
elif isinstance(constraint, FixBondLengths):
|
|
119
|
+
pairs = np.asarray(constraint.pairs, dtype=int)
|
|
120
|
+
if constraint.bondlengths is None:
|
|
121
|
+
pos = atoms.get_positions()
|
|
122
|
+
rij = pos[pairs[:, 0]] - pos[pairs[:, 1]]
|
|
123
|
+
ref_distances = np.linalg.norm(rij, axis=1)
|
|
124
|
+
else:
|
|
125
|
+
ref_distances = np.asarray(constraint.bondlengths, dtype=float)
|
|
126
|
+
calculators.append(
|
|
127
|
+
HarmonicFixBondLengths(pairs, ref_distances, k_bond=k_bond)
|
|
128
|
+
)
|
|
129
|
+
remaining_constraints.append(constraint)
|
|
130
|
+
else:
|
|
131
|
+
warnings.warn(
|
|
132
|
+
f"Unsupported ASE constraint {type(constraint).__name__}; "
|
|
133
|
+
"ignored for harmonic restraints."
|
|
134
|
+
)
|
|
135
|
+
remaining_constraints.append(constraint)
|
|
136
|
+
if remaining_constraints != constraints:
|
|
137
|
+
atoms.set_constraint(remaining_constraints or None)
|
|
138
|
+
return calculators
|
|
139
|
+
|
|
140
|
+
|
mlmm/hessian_cache.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""In-process Hessian cache for the ``all`` workflow.
|
|
2
|
+
|
|
3
|
+
Stores Hessian matrices as CPU tensors (or numpy arrays) in a module-level
|
|
4
|
+
dict so that subsequent CLI stages executed via ``_run_cli_main()`` can
|
|
5
|
+
reuse them without recomputation.
|
|
6
|
+
|
|
7
|
+
Each entry may carry ``active_dofs`` — a list of DOF indices that the stored
|
|
8
|
+
(partial) Hessian spans. Consumers set ``geometry.within_partial_hessian``
|
|
9
|
+
before assigning partial Hessians to ``geometry.cart_hessian``.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import torch
|
|
14
|
+
from typing import Any, Dict, Optional, Sequence
|
|
15
|
+
|
|
16
|
+
_cache: Dict[str, Any] = {}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def store(
|
|
20
|
+
key: str,
|
|
21
|
+
H,
|
|
22
|
+
active_dofs: Optional[Sequence[int]] = None,
|
|
23
|
+
meta: Optional[dict] = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Cache a (possibly partial) Hessian on CPU."""
|
|
26
|
+
if isinstance(H, torch.Tensor):
|
|
27
|
+
h_cpu = H.detach().cpu().clone()
|
|
28
|
+
else:
|
|
29
|
+
h_cpu = np.array(H, copy=True)
|
|
30
|
+
_cache[key] = {
|
|
31
|
+
"hessian": h_cpu,
|
|
32
|
+
"active_dofs": list(active_dofs) if active_dofs is not None else None,
|
|
33
|
+
"meta": meta or {},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load(key: str) -> Optional[Dict[str, Any]]:
|
|
38
|
+
"""Return cached entry or *None*."""
|
|
39
|
+
return _cache.get(key)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def clear() -> None:
|
|
43
|
+
"""Drop all cached Hessians."""
|
|
44
|
+
_cache.clear()
|
mlmm/hessian_calc.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Hessian utilities and vibrational analysis helpers for the ML/MM workflow.
|
|
2
|
+
|
|
3
|
+
These routines construct Hessians, compute frequencies and handle I/O.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from contextlib import nullcontext
|
|
8
|
+
import numpy as np
|
|
9
|
+
import time
|
|
10
|
+
import torch
|
|
11
|
+
from typing import Sequence
|
|
12
|
+
from ase.data import atomic_masses
|
|
13
|
+
from ase.constraints import FixAtoms
|
|
14
|
+
from ase.atoms import Atoms
|
|
15
|
+
from pysisyphus.constants import AMU2AU
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def clone_atoms(atoms):
|
|
19
|
+
"""Return a shallow copy of *atoms* keeping constraints (no PBC)."""
|
|
20
|
+
new = Atoms(
|
|
21
|
+
symbols=atoms.get_chemical_symbols(),
|
|
22
|
+
positions=atoms.get_positions(),
|
|
23
|
+
pbc=False,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if atoms.constraints:
|
|
27
|
+
fix_idx = [
|
|
28
|
+
i for c in atoms.constraints if isinstance(c, FixAtoms)
|
|
29
|
+
for i in c.get_indices()
|
|
30
|
+
]
|
|
31
|
+
if fix_idx:
|
|
32
|
+
new.set_constraint(FixAtoms(indices=fix_idx))
|
|
33
|
+
|
|
34
|
+
return new
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def hessian_calc(
|
|
38
|
+
atoms,
|
|
39
|
+
calc,
|
|
40
|
+
delta: float = 0.01,
|
|
41
|
+
info_path: str | None = None,
|
|
42
|
+
*,
|
|
43
|
+
dtype: np.dtype = np.float64,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Numerically compute the full Cartesian Hessian (second derivatives).
|
|
47
|
+
|
|
48
|
+
Finite differences are taken by displacing each *unfixed* atom by
|
|
49
|
+
``±delta`` Å along x, y, z. Central differences give the sub-Hessian,
|
|
50
|
+
which is **always expanded to the full (3N, 3N) matrix** by padding
|
|
51
|
+
zeros for fixed atoms before returning.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
atoms : ase.Atoms
|
|
56
|
+
Structure to differentiate.
|
|
57
|
+
calc : ase.Calculator
|
|
58
|
+
Calculator providing forces.
|
|
59
|
+
delta : float, default 0.01 Å
|
|
60
|
+
Displacement size.
|
|
61
|
+
info_path : str | None
|
|
62
|
+
If given, progress info is appended to this file.
|
|
63
|
+
dtype : numpy dtype, default float64
|
|
64
|
+
Data type of the returned Hessian.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
ndarray
|
|
69
|
+
Cartesian Hessian of shape ``(3N, 3N)`` where
|
|
70
|
+
``N == len(atoms)`` (fixed-atom rows/cols are zero).
|
|
71
|
+
"""
|
|
72
|
+
fixed = {
|
|
73
|
+
i for c in atoms.constraints if isinstance(c, FixAtoms)
|
|
74
|
+
for i in c.get_indices()
|
|
75
|
+
}
|
|
76
|
+
movable = np.asarray([i for i in range(len(atoms)) if i not in fixed])
|
|
77
|
+
m = len(movable)
|
|
78
|
+
n_dof = 3 * m
|
|
79
|
+
|
|
80
|
+
# Short-circuit: all atoms frozen → return zeros
|
|
81
|
+
if m == 0:
|
|
82
|
+
N = len(atoms)
|
|
83
|
+
return np.zeros((3 * N, 3 * N))
|
|
84
|
+
|
|
85
|
+
H_sub = np.empty((n_dof, n_dof), dtype=dtype)
|
|
86
|
+
row = 0
|
|
87
|
+
|
|
88
|
+
log_cm = nullcontext(None)
|
|
89
|
+
if info_path is not None:
|
|
90
|
+
os.makedirs(os.path.dirname(info_path), exist_ok=True)
|
|
91
|
+
log_cm = open(info_path, "w", encoding="utf-8")
|
|
92
|
+
|
|
93
|
+
with log_cm as log:
|
|
94
|
+
if info_path is not None:
|
|
95
|
+
log.write("Hessian calculation by numerical differentiation\n")
|
|
96
|
+
log.write("------------------------------------------------\n")
|
|
97
|
+
t0 = time.time()
|
|
98
|
+
checkpoints = [int(m * k / 10) for k in range(1, 11)] # every 10 %
|
|
99
|
+
|
|
100
|
+
for count, a in enumerate(movable, start=1):
|
|
101
|
+
for coord in range(3):
|
|
102
|
+
plus = clone_atoms(atoms)
|
|
103
|
+
plus.calc = calc
|
|
104
|
+
plus.positions[a, coord] += delta
|
|
105
|
+
Fp = plus.calc.get_forces(plus)
|
|
106
|
+
|
|
107
|
+
minus = clone_atoms(atoms)
|
|
108
|
+
minus.calc = calc
|
|
109
|
+
minus.positions[a, coord] -= delta
|
|
110
|
+
Fm = minus.calc.get_forces(minus)
|
|
111
|
+
|
|
112
|
+
# Central difference: −∂F/∂x = ∂²E/∂x²
|
|
113
|
+
H_sub[row] = (Fm - Fp)[movable].ravel() / (2 * delta)
|
|
114
|
+
row += 1
|
|
115
|
+
|
|
116
|
+
if info_path is not None and count in checkpoints:
|
|
117
|
+
pct = checkpoints.index(count) * 10 + 10
|
|
118
|
+
elapsed = time.time() - t0
|
|
119
|
+
total_est = elapsed * 100.0 / pct
|
|
120
|
+
remaining = total_est - elapsed
|
|
121
|
+
me, se = divmod(int(elapsed), 60)
|
|
122
|
+
mr, sr = divmod(int(remaining), 60)
|
|
123
|
+
log.write(
|
|
124
|
+
f"{pct:3d}% ({count}/{m}) Elapsed {me}m{se}s ETA {mr}m{sr}s\n"
|
|
125
|
+
)
|
|
126
|
+
log.flush()
|
|
127
|
+
|
|
128
|
+
if info_path is not None:
|
|
129
|
+
elapsed = time.time() - t0
|
|
130
|
+
me, se = divmod(int(elapsed), 60)
|
|
131
|
+
log.write(f"Done in {me}m{se}s\n")
|
|
132
|
+
|
|
133
|
+
N = len(atoms)
|
|
134
|
+
H = np.zeros((3 * N, 3 * N), dtype=dtype)
|
|
135
|
+
for i_local, i_atom in enumerate(movable):
|
|
136
|
+
for j_local, j_atom in enumerate(movable):
|
|
137
|
+
H[
|
|
138
|
+
3 * i_atom : 3 * i_atom + 3,
|
|
139
|
+
3 * j_atom : 3 * j_atom + 3,
|
|
140
|
+
] = H_sub[
|
|
141
|
+
3 * i_local : 3 * i_local + 3,
|
|
142
|
+
3 * j_local : 3 * j_local + 3,
|
|
143
|
+
]
|
|
144
|
+
return H
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _get_masses(Z):
|
|
148
|
+
"""Return atomic masses (amu) for a list/array of atomic numbers."""
|
|
149
|
+
return np.array([atomic_masses[z] for z in Z])
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _build_tr_basis(coords_bohr: torch.Tensor, masses_amu: torch.Tensor) -> torch.Tensor:
|
|
153
|
+
"""Return (3N,6) mass-weighted basis for Tx,Ty,Tz,Rx,Ry,Rz."""
|
|
154
|
+
device, dtype = coords_bohr.device, coords_bohr.dtype
|
|
155
|
+
N = coords_bohr.shape[0]
|
|
156
|
+
|
|
157
|
+
m_au = masses_amu.to(dtype=dtype, device=device) * AMU2AU
|
|
158
|
+
m_sqrt = torch.sqrt(m_au).reshape(-1, 1)
|
|
159
|
+
|
|
160
|
+
com = (m_au.reshape(-1, 1) * coords_bohr).sum(0) / m_au.sum()
|
|
161
|
+
x = coords_bohr - com
|
|
162
|
+
|
|
163
|
+
eye3 = torch.eye(3, dtype=dtype, device=device)
|
|
164
|
+
cols = []
|
|
165
|
+
|
|
166
|
+
for i in range(3): # Tx, Ty, Tz
|
|
167
|
+
cols.append((eye3[i].repeat(N, 1) * m_sqrt).reshape(-1, 1))
|
|
168
|
+
for i in range(3): # Rx, Ry, Rz
|
|
169
|
+
rot = torch.cross(x, eye3[i].expand_as(x), dim=1) * m_sqrt
|
|
170
|
+
cols.append(rot.reshape(-1, 1))
|
|
171
|
+
|
|
172
|
+
return torch.cat(cols, dim=1)
|
|
173
|
+
|
|
174
|
+
|