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,69 @@
|
|
|
1
|
+
from math import sin
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from pysisyphus.intcoords.Primitive import Primitive
|
|
6
|
+
from pysisyphus.intcoords.derivatives import d2q_a
|
|
7
|
+
from pysisyphus.linalg import cross3, norm3
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Bend(Primitive):
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def _weight(atoms, coords3d, indices, f_damping):
|
|
14
|
+
m, o, n = indices
|
|
15
|
+
rho_mo = Bend.rho(atoms, coords3d, (m, o))
|
|
16
|
+
rho_on = Bend.rho(atoms, coords3d, (o, n))
|
|
17
|
+
rad = Bend._calculate(coords3d, indices)
|
|
18
|
+
return (rho_mo * rho_on)**0.5 * (f_damping + (1-f_damping)*sin(rad))
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def _calculate(coords3d, indices, gradient=False):
|
|
22
|
+
m, o, n = indices
|
|
23
|
+
u_dash = coords3d[m] - coords3d[o]
|
|
24
|
+
v_dash = coords3d[n] - coords3d[o]
|
|
25
|
+
u_norm = norm3(u_dash)
|
|
26
|
+
v_norm = norm3(v_dash)
|
|
27
|
+
u = u_dash / u_norm
|
|
28
|
+
v = v_dash / v_norm
|
|
29
|
+
|
|
30
|
+
udv = max(-1.0, min(1.0, u.dot(v)))
|
|
31
|
+
angle_rad = np.arccos(udv)
|
|
32
|
+
|
|
33
|
+
if gradient:
|
|
34
|
+
cross_vec1 = ( 1, -1, 1)
|
|
35
|
+
cross_vec2 = (-1, 1, 1)
|
|
36
|
+
|
|
37
|
+
# Determine second vector for the cross product, to get an
|
|
38
|
+
# orthogonal direction. Eq. (24) in [1]
|
|
39
|
+
uv_parallel = Bend.parallel(u, v)
|
|
40
|
+
if not uv_parallel:
|
|
41
|
+
cross_vec = v
|
|
42
|
+
elif not Bend.parallel(u, cross_vec1):
|
|
43
|
+
cross_vec = cross_vec1
|
|
44
|
+
else:
|
|
45
|
+
cross_vec = cross_vec2
|
|
46
|
+
|
|
47
|
+
w_dash = cross3(u, cross_vec)
|
|
48
|
+
w = w_dash / norm3(w_dash)
|
|
49
|
+
|
|
50
|
+
uxw = cross3(u, w)
|
|
51
|
+
wxv = cross3(w, v)
|
|
52
|
+
|
|
53
|
+
row = np.zeros_like(coords3d)
|
|
54
|
+
# | m | n | o |
|
|
55
|
+
# -----------------------------------
|
|
56
|
+
# sign_factor(amo) | 1 | 0 | -1 | first_term
|
|
57
|
+
# sign_factor(ano) | 0 | 1 | -1 | second_term
|
|
58
|
+
first_term = uxw / u_norm
|
|
59
|
+
second_term = wxv / v_norm
|
|
60
|
+
row[m,:] = first_term
|
|
61
|
+
row[o,:] = -first_term - second_term
|
|
62
|
+
row[n,:] = second_term
|
|
63
|
+
row = row.flatten()
|
|
64
|
+
return angle_rad, row
|
|
65
|
+
return angle_rad
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def _jacobian(coords3d, indices):
|
|
69
|
+
return d2q_a(*coords3d[indices].flatten())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# See
|
|
2
|
+
# https://www.jwwalker.com/pages/angle-between-vectors.html
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from pysisyphus.intcoords.Bend import Bend
|
|
7
|
+
from pysisyphus.intcoords.derivatives import q_a2, dq_a2, d2q_a2
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Bend2(Bend):
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def _calculate(coords3d, indices, gradient=False):
|
|
14
|
+
args = coords3d[indices].flatten()
|
|
15
|
+
val = q_a2(*args)
|
|
16
|
+
if gradient:
|
|
17
|
+
grad = dq_a2(*args).reshape(-1, 3)
|
|
18
|
+
row = np.zeros_like(coords3d)
|
|
19
|
+
row[indices] = grad
|
|
20
|
+
return val, row.flatten()
|
|
21
|
+
return val
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def _jacobian(coords3d, indices):
|
|
25
|
+
return d2q_a2(*coords3d[indices].flatten())
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pysisyphus.intcoords.Primitive import Primitive
|
|
4
|
+
from pysisyphus.linalg import norm3
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BondedFragment(Primitive):
|
|
8
|
+
def __init__(self, indices, bond_indices, **kwargs):
|
|
9
|
+
from_frag, to_ = bond_indices
|
|
10
|
+
assert from_frag in indices
|
|
11
|
+
assert to_ not in indices
|
|
12
|
+
self.bond_indices = list(bond_indices)
|
|
13
|
+
kwargs["calc_kwargs"] = ("bond_indices",)
|
|
14
|
+
super().__init__(indices, **kwargs)
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def _weight(atoms, coords3d, indices, f_damping):
|
|
18
|
+
return 1
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def _calculate(coords3d, indices, gradient=False, bond_indices=None):
|
|
22
|
+
from_frag, to_ = bond_indices
|
|
23
|
+
bond = coords3d[to_] - coords3d[from_frag]
|
|
24
|
+
value = norm3(bond)
|
|
25
|
+
|
|
26
|
+
if gradient:
|
|
27
|
+
bond_normed = bond / value
|
|
28
|
+
row = np.zeros_like(coords3d)
|
|
29
|
+
row[indices, :] = -bond_normed
|
|
30
|
+
row = row.flatten()
|
|
31
|
+
return value, row
|
|
32
|
+
return value
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pysisyphus.intcoords.Primitive import Primitive
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Cartesian(Primitive):
|
|
7
|
+
|
|
8
|
+
def __init__(self, *args, **kwargs):
|
|
9
|
+
kwargs["calc_kwargs"] = ("cart_axis",)
|
|
10
|
+
super().__init__(*args, **kwargs)
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def _weight(atoms, coords3d, indices, f_damping):
|
|
14
|
+
return 1
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def _calculate(coords3d, indices, gradient=False, cart_axis=0):
|
|
18
|
+
m, = indices
|
|
19
|
+
value = coords3d[m, cart_axis]
|
|
20
|
+
if gradient:
|
|
21
|
+
row = np.zeros_like(coords3d)
|
|
22
|
+
row[m, cart_axis] = 1
|
|
23
|
+
row = row.flatten()
|
|
24
|
+
return value, row
|
|
25
|
+
return value
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def _jacobian(coords3d, indices, cart_axis):
|
|
29
|
+
return np.zeros(1)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CartesianX(Cartesian):
|
|
33
|
+
cart_axis = 0
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CartesianY(Cartesian):
|
|
37
|
+
cart_axis = 1
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CartesianZ(Cartesian):
|
|
41
|
+
cart_axis = 2
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import torch
|
|
6
|
+
from numpy.typing import NDArray
|
|
7
|
+
|
|
8
|
+
from pysisyphus.intcoords.Coords import CoordSys
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CartesianCoords(CoordSys):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
atoms,
|
|
15
|
+
coords3d: NDArray,
|
|
16
|
+
masses,
|
|
17
|
+
freeze_atoms=None,
|
|
18
|
+
*,
|
|
19
|
+
mass_weighted=False,
|
|
20
|
+
**kwargs,
|
|
21
|
+
):
|
|
22
|
+
for key, val in kwargs.items():
|
|
23
|
+
# f-string don't seem to work when pytest reports the warnings after a test
|
|
24
|
+
# run, so we construct to warning before it is issued.
|
|
25
|
+
msg = (
|
|
26
|
+
# Also printing the value is too much
|
|
27
|
+
# f"Keyword '{key}': '{val}' is not supported by this coordinate system!"
|
|
28
|
+
f"Keyword '{key}' is not supported by this coordinate system!"
|
|
29
|
+
)
|
|
30
|
+
warnings.warn(msg)
|
|
31
|
+
self.atoms = atoms
|
|
32
|
+
self._coords3d = coords3d
|
|
33
|
+
self.atom_num = len(self.atoms)
|
|
34
|
+
self.masses = masses
|
|
35
|
+
if freeze_atoms is None:
|
|
36
|
+
freeze_atoms = list()
|
|
37
|
+
self.freeze_atoms = freeze_atoms
|
|
38
|
+
self.mass_weighted = mass_weighted
|
|
39
|
+
|
|
40
|
+
self.move_mask = np.full(self.atom_num, True, dtype=bool)
|
|
41
|
+
self.move_mask[self.freeze_atoms] = False
|
|
42
|
+
self.move_mask_rep = np.repeat(self.move_mask, 3)
|
|
43
|
+
self.zero_vec = np.zeros_like(coords3d)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def masses(self) -> NDArray:
|
|
47
|
+
return self._masses
|
|
48
|
+
|
|
49
|
+
@masses.setter
|
|
50
|
+
def masses(self, masses: NDArray):
|
|
51
|
+
assert len(masses) == self.atom_num
|
|
52
|
+
masses = np.array(masses, dtype=float)
|
|
53
|
+
self._masses = masses
|
|
54
|
+
# Also precalculate masses' square roots for mass-weighting
|
|
55
|
+
self._masses_sqrt = np.sqrt(self.masses)
|
|
56
|
+
# and its inverse
|
|
57
|
+
self._inv_masses_rep_sqrt = 1 / np.repeat(self.masses_sqrt, 3)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def masses_sqrt(self) -> NDArray:
|
|
61
|
+
return self._masses_sqrt
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def inv_masses_rep_sqrt(self) -> NDArray:
|
|
65
|
+
return self._inv_masses_rep_sqrt
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def coords(self) -> NDArray:
|
|
69
|
+
coords3d = self.coords3d.copy()
|
|
70
|
+
if self.mass_weighted:
|
|
71
|
+
coords3d *= self.masses_sqrt[:, None]
|
|
72
|
+
coords3d = coords3d[self.move_mask]
|
|
73
|
+
return coords3d.flatten()
|
|
74
|
+
|
|
75
|
+
def transform_forces(self, cart_forces: NDArray) -> NDArray:
|
|
76
|
+
forces = cart_forces.reshape(-1, 3)
|
|
77
|
+
if self.mass_weighted:
|
|
78
|
+
forces /= self.masses_sqrt[:, None]
|
|
79
|
+
forces = forces[self.move_mask]
|
|
80
|
+
return forces.flatten()
|
|
81
|
+
|
|
82
|
+
def transform_hessian(
|
|
83
|
+
self, cart_hessian: NDArray, int_gradient: Optional[NDArray] = None
|
|
84
|
+
):
|
|
85
|
+
if isinstance(cart_hessian, torch.Tensor):
|
|
86
|
+
if self.mass_weighted:
|
|
87
|
+
inv_masses = torch.tensor(
|
|
88
|
+
self.inv_masses_rep_sqrt,
|
|
89
|
+
dtype=cart_hessian.dtype,
|
|
90
|
+
device=cart_hessian.device,
|
|
91
|
+
)
|
|
92
|
+
M_inv = torch.diag(inv_masses)
|
|
93
|
+
cart_hessian = M_inv @ cart_hessian @ M_inv
|
|
94
|
+
mask = torch.tensor(
|
|
95
|
+
self.move_mask_rep, dtype=torch.bool, device=cart_hessian.device
|
|
96
|
+
)
|
|
97
|
+
cart_hessian = cart_hessian[mask][:, mask]
|
|
98
|
+
return cart_hessian
|
|
99
|
+
if self.mass_weighted:
|
|
100
|
+
M_inv = np.diag(self.inv_masses_rep_sqrt)
|
|
101
|
+
cart_hessian = M_inv @ cart_hessian @ M_inv
|
|
102
|
+
cart_hessian = cart_hessian[self.move_mask_rep][:, self.move_mask_rep]
|
|
103
|
+
return cart_hessian
|
|
104
|
+
|
|
105
|
+
def transform_int_step(
|
|
106
|
+
self,
|
|
107
|
+
step: NDArray,
|
|
108
|
+
update_constraints: Optional[bool] = False,
|
|
109
|
+
pure: Optional[bool] = False,
|
|
110
|
+
) -> NDArray:
|
|
111
|
+
if update_constraints:
|
|
112
|
+
raise Exception("update_constraints is currently ignored!")
|
|
113
|
+
full_step = self.zero_vec.copy()
|
|
114
|
+
full_step[self.move_mask] = step.reshape(-1, 3)
|
|
115
|
+
if self.mass_weighted:
|
|
116
|
+
full_step /= self.masses_sqrt[:, None]
|
|
117
|
+
if not pure:
|
|
118
|
+
self.coords3d += full_step
|
|
119
|
+
return full_step.flatten()
|
|
120
|
+
|
|
121
|
+
def project_hessian(self, hessian):
|
|
122
|
+
return hessian
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def coords3d(self) -> NDArray:
|
|
126
|
+
return self._coords3d
|
|
127
|
+
|
|
128
|
+
@coords3d.setter
|
|
129
|
+
def coords3d(self, coords3d: NDArray):
|
|
130
|
+
self._coords3d = coords3d.reshape(-1, 3)
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def typed_prims(self) -> List:
|
|
134
|
+
return list()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class MWCartesianCoords(CartesianCoords):
|
|
138
|
+
def __init__(self, *args, **kwargs):
|
|
139
|
+
kwargs["mass_weighted"] = True
|
|
140
|
+
super().__init__(*args, **kwargs)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import List, Optional, Protocol
|
|
3
|
+
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CoordSys(Protocol):
|
|
8
|
+
@abstractmethod
|
|
9
|
+
def transform_forces(self, cart_forces: NDArray) -> NDArray:
|
|
10
|
+
"""Transform Cartesian forces to this coordinate system."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def transform_hessian(
|
|
15
|
+
self, cart_hessian: NDArray, int_gradient: Optional[NDArray]
|
|
16
|
+
) -> NDArray:
|
|
17
|
+
"""Transform Cartesian Hessian to this coordinate system."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def transform_int_step(
|
|
22
|
+
self, step: NDArray, update_constraints: bool = False, pure: bool = False
|
|
23
|
+
) -> NDArray:
|
|
24
|
+
"""Transform step in this coordinate system to Cartesians."""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def project_hessian(self, hessian: NDArray) -> NDArray:
|
|
29
|
+
"""Project Hessian in the current coordinate system."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def coords(self) -> NDArray:
|
|
35
|
+
"""Getter for coordinates in this coordinate system."""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def coords3d(self) -> NDArray:
|
|
41
|
+
"""Getter for 3d Cartesian coordinates."""
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
@coords3d.setter
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def coords3d(self, coords3d: NDArray):
|
|
47
|
+
"""Setter for 3d Cartesian coordinates."""
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def typed_prims(self) -> List:
|
|
53
|
+
"""List of (primitive) internal coordinates.
|
|
54
|
+
|
|
55
|
+
May be empty, e.g., when the coordinate system is Cartesian."""
|
|
56
|
+
pass
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# [1] https://aip.scitation.org/doi/10.1063/1.471864
|
|
2
|
+
# The generation and use of delocalized internal coordinates in geometry
|
|
3
|
+
# optimization.
|
|
4
|
+
# Baker, 1996
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
from pysisyphus.intcoords import RedundantCoords
|
|
10
|
+
from pysisyphus.linalg import gram_schmidt
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DLC(RedundantCoords):
|
|
14
|
+
def __init__(self, *args, full_set=True, **kwargs):
|
|
15
|
+
super().__init__(*args, **kwargs)
|
|
16
|
+
|
|
17
|
+
self.full_set = full_set
|
|
18
|
+
self.set_active_set()
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def U(self):
|
|
22
|
+
return self._U
|
|
23
|
+
|
|
24
|
+
@U.setter
|
|
25
|
+
def U(self, U):
|
|
26
|
+
self._U = U
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def constraints(self):
|
|
30
|
+
return self._constraints
|
|
31
|
+
|
|
32
|
+
@constraints.setter
|
|
33
|
+
def constraints(self, constraints):
|
|
34
|
+
self.U = self.U_unconstrained.copy()
|
|
35
|
+
constraints = np.array(constraints)
|
|
36
|
+
constraints.flags.writeable = False
|
|
37
|
+
# Constraint columns should be same length as columns of U.
|
|
38
|
+
assert constraints.shape[0] == self.U.shape[0]
|
|
39
|
+
self._constraints = constraints
|
|
40
|
+
U_constrained = self.get_constrained_U(self._constraints)
|
|
41
|
+
|
|
42
|
+
# # Replace the constraint-columns with zeros vectors, so the total
|
|
43
|
+
# # number of coords doesn't change. This also zeros any force components
|
|
44
|
+
# # that belong to the constraints.
|
|
45
|
+
# zero_arr = np.zeros_like(constraints)
|
|
46
|
+
# self.U = np.concatenate((zero_arr, U_constrained), axis=1)
|
|
47
|
+
U_constrained = np.concatenate((constraints, U_constrained), axis=1)
|
|
48
|
+
self.U = U_constrained
|
|
49
|
+
|
|
50
|
+
def reset_constraints(self):
|
|
51
|
+
self._constraints = tuple()
|
|
52
|
+
self.U = self.U_unconstrained
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def coords(self):
|
|
56
|
+
return self.U.T.dot(self.prim_coords)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def B(self):
|
|
60
|
+
"""Wilson B-Matrix in the non-redundant subspace."""
|
|
61
|
+
return self.U.T.dot(self.B_prim)
|
|
62
|
+
|
|
63
|
+
def project_hessian(self, H):
|
|
64
|
+
"""As we already work in the non-redundant subspace we don't have
|
|
65
|
+
to project/shift the hessian as we do it for simple redundant
|
|
66
|
+
internal coordinates."""
|
|
67
|
+
return H
|
|
68
|
+
|
|
69
|
+
def transform_hessian(self, cart_hessian, int_gradient=None):
|
|
70
|
+
"""Transform Cartesian Hessian to DLC."""
|
|
71
|
+
# Transform the DLC gradient to primitive coordinates
|
|
72
|
+
if isinstance(cart_hessian, torch.Tensor):
|
|
73
|
+
U = self._as_torch_like(self.U, cart_hessian)
|
|
74
|
+
if int_gradient is not None:
|
|
75
|
+
int_gradient = self._as_torch_like(int_gradient, cart_hessian)
|
|
76
|
+
prim_gradient = (U * int_gradient).sum(dim=1)
|
|
77
|
+
else:
|
|
78
|
+
prim_gradient = None
|
|
79
|
+
H = super().transform_hessian(cart_hessian, prim_gradient)
|
|
80
|
+
return U.T @ H @ U
|
|
81
|
+
if int_gradient is not None:
|
|
82
|
+
prim_gradient = (self.U * int_gradient).sum(axis=1)
|
|
83
|
+
else:
|
|
84
|
+
prim_gradient = None
|
|
85
|
+
H = super().transform_hessian(cart_hessian, prim_gradient)
|
|
86
|
+
return self.U.T.dot(H).dot(self.U)
|
|
87
|
+
|
|
88
|
+
def backtransform_hessian(self, *args, **kwargs):
|
|
89
|
+
raise Exception("Check if we can just use the parents method.")
|
|
90
|
+
|
|
91
|
+
def transform_int_step(self, step, *args, **kwargs):
|
|
92
|
+
"""As the transformation is done in primitive internal coordinates
|
|
93
|
+
we convert the DLC back to primitive coordinates."""
|
|
94
|
+
prim_step = (step * self.U).sum(axis=1)
|
|
95
|
+
return super().transform_int_step(prim_step, *args, **kwargs)
|
|
96
|
+
|
|
97
|
+
def get_active_set(self, B, inv_thresh=None):
|
|
98
|
+
"""See [5] between Eq. (7) and Eq. (8) for advice regarding
|
|
99
|
+
the threshold."""
|
|
100
|
+
if self.weighted:
|
|
101
|
+
weights = np.array(
|
|
102
|
+
[prim.weight(self.atoms, self.coords3d) for prim in self.primitives]
|
|
103
|
+
)
|
|
104
|
+
self.log(
|
|
105
|
+
"Weighting B-matrix:\n"
|
|
106
|
+
f"\tWeights: {np.array2string(weights, precision=4)}\n"
|
|
107
|
+
f"\tmax(weights)={weights.max():.4f}, "
|
|
108
|
+
f"min(weights)={weights.min():.4f}, ({len(weights)} primitives)"
|
|
109
|
+
)
|
|
110
|
+
B = np.diag(weights).dot(B)
|
|
111
|
+
|
|
112
|
+
G = B.dot(B.T)
|
|
113
|
+
eigvals, eigvectors = np.linalg.eigh(G)
|
|
114
|
+
|
|
115
|
+
if inv_thresh is None:
|
|
116
|
+
# The singular values of G=B B^T correspond to the square roots of the
|
|
117
|
+
# eigenvalues of G.
|
|
118
|
+
#
|
|
119
|
+
# S = sqrt(w)
|
|
120
|
+
# w = S**2
|
|
121
|
+
#
|
|
122
|
+
# To stay consistent with the SVD, we derive the eigenvalue threshold from
|
|
123
|
+
# the SVD threshold.
|
|
124
|
+
inv_thresh = self.svd_inv_thresh ** 2
|
|
125
|
+
|
|
126
|
+
if self.full_set:
|
|
127
|
+
use_inds = np.full_like(eigvals, False, dtype=bool)
|
|
128
|
+
dof = 3 * len(self.atoms) - 6
|
|
129
|
+
use_inds[-dof:] = True
|
|
130
|
+
else:
|
|
131
|
+
use_inds = np.abs(eigvals) > inv_thresh
|
|
132
|
+
use_eigvals = eigvals[use_inds]
|
|
133
|
+
min_eigval = use_eigvals.min()
|
|
134
|
+
self.log(
|
|
135
|
+
f"Diagonalizing G yielded {use_inds.sum()} DLCs. Smallest eigenvalue "
|
|
136
|
+
f"is {min_eigval:.4e}"
|
|
137
|
+
)
|
|
138
|
+
return eigvectors[:, use_inds]
|
|
139
|
+
|
|
140
|
+
def set_active_set(self):
|
|
141
|
+
self.U = self.get_active_set(self.B_prim)
|
|
142
|
+
# Keep a copy of the original active set, in case self.U gets
|
|
143
|
+
# modified by constraint application.
|
|
144
|
+
self.U_unconstrained = self.U.copy()
|
|
145
|
+
self.original_U_shape = self.U_unconstrained.shape
|
|
146
|
+
self._constraints = tuple()
|
|
147
|
+
|
|
148
|
+
def project_primitive_on_active_set(self, prim_ind):
|
|
149
|
+
prim_vec = np.zeros(self.U.shape[0])
|
|
150
|
+
prim_vec[prim_ind] = 1
|
|
151
|
+
c_proj = (np.einsum("i,ij->j", prim_vec, self.U) * self.U).sum(axis=1)
|
|
152
|
+
c_proj /= np.linalg.norm(c_proj)
|
|
153
|
+
return c_proj
|
|
154
|
+
|
|
155
|
+
def get_constrained_U(self, constraint_vecs):
|
|
156
|
+
# Constraints are organized in columns
|
|
157
|
+
constr_num = constraint_vecs.shape[1]
|
|
158
|
+
V = np.concatenate((constraint_vecs, self.U), axis=1)
|
|
159
|
+
orthonormalized = gram_schmidt(V.T).T
|
|
160
|
+
# During Gram-Schmidt a number of columns of U should have
|
|
161
|
+
# dropped out. They are replaced by the constraint_vecs, so
|
|
162
|
+
# the total shape of should not change.
|
|
163
|
+
assert orthonormalized.shape[1] == self.original_U_shape[1]
|
|
164
|
+
# Remove constraint vectors
|
|
165
|
+
# [1] states that we somehow have to keep the constraint vectors
|
|
166
|
+
# (or the corresponding unit vectors) for the iterative
|
|
167
|
+
# back-transformation. Right now I don't understand why we would
|
|
168
|
+
# have to do this ([1], p. 10 (200), right column).
|
|
169
|
+
U_proj = orthonormalized[:, constr_num:]
|
|
170
|
+
return U_proj
|
|
171
|
+
|
|
172
|
+
def freeze_primitives(self, typed_prims):
|
|
173
|
+
"""Freeze primitive internal coordinates.
|
|
174
|
+
|
|
175
|
+
Parameters
|
|
176
|
+
----------
|
|
177
|
+
typed_prims : iterable of typed primitives
|
|
178
|
+
Iterable containing typed_primitives, starting with a PrimType and
|
|
179
|
+
followed by atom indices.
|
|
180
|
+
"""
|
|
181
|
+
prim_indices = [self.get_index_of_typed_prim(tp) for tp in typed_prims]
|
|
182
|
+
not_defined = [
|
|
183
|
+
tp for tp, prim_ind in zip(typed_prims, prim_indices) if prim_ind is None
|
|
184
|
+
]
|
|
185
|
+
assert (
|
|
186
|
+
None not in prim_indices
|
|
187
|
+
), f"Some primitive internals are not defined ({not_defined})!"
|
|
188
|
+
projected_primitives = np.array(
|
|
189
|
+
[self.project_primitive_on_active_set(pi) for pi in prim_indices]
|
|
190
|
+
).T
|
|
191
|
+
self.constraints = projected_primitives
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class HDLC(DLC):
|
|
195
|
+
def __init__(self, *args, **kwargs):
|
|
196
|
+
kwargs["hybrid"] = True
|
|
197
|
+
super().__init__(*args, **kwargs)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pysisyphus.intcoords.Primitive import Primitive
|
|
2
|
+
from pysisyphus.intcoords.Stretch import Stretch
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DistanceFunction(Primitive):
|
|
6
|
+
def __init__(self, indices, coeff=-1, **kwargs):
|
|
7
|
+
self.coeff = float(coeff)
|
|
8
|
+
kwargs["calc_kwargs"] = ("coeff",)
|
|
9
|
+
super().__init__(indices, **kwargs)
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def _weight(atoms, coords3d, indices, f_damping):
|
|
13
|
+
return 1
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def _calculate(coords3d, indices, gradient=False, coeff=-1):
|
|
17
|
+
"""
|
|
18
|
+
m--n o--p
|
|
19
|
+
"""
|
|
20
|
+
m, n, o, p = indices
|
|
21
|
+
val1, grad1 = Stretch._calculate(coords3d, (m, n), gradient=True)
|
|
22
|
+
val2, grad2 = Stretch._calculate(coords3d, (o, p), gradient=True)
|
|
23
|
+
val = val1 + coeff * val2
|
|
24
|
+
grad = grad1 + coeff * grad2
|
|
25
|
+
if gradient:
|
|
26
|
+
return val, grad
|
|
27
|
+
return val
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _jacobian(coords3d, indices, coeff):
|
|
31
|
+
m, n, o, p = indices
|
|
32
|
+
jac1 = Stretch._jacobian(coords3d, [m, n])
|
|
33
|
+
jac2 = Stretch._jacobian(coords3d, [o, p])
|
|
34
|
+
return jac1 + coeff * jac2
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pysisyphus.intcoords.Torsion import Torsion
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DummyImproper(Torsion):
|
|
7
|
+
def __init__(self, indices, *args, fix_inner=True, **kwargs):
|
|
8
|
+
self.fix_inner = fix_inner
|
|
9
|
+
kwargs["calc_kwargs"] = ("fix_inner",)
|
|
10
|
+
super().__init__(indices, *args, **kwargs)
|
|
11
|
+
self.log("DummyImproper is never checked for collinear atoms!")
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def _get_dummy_coords(coords3d, indices, r=1.889):
|
|
15
|
+
r"""
|
|
16
|
+
dummy -> D\
|
|
17
|
+
atomy | \
|
|
18
|
+
| \
|
|
19
|
+
A---B---C
|
|
20
|
+
"""
|
|
21
|
+
a, b, c = indices
|
|
22
|
+
# Center
|
|
23
|
+
B = coords3d[b]
|
|
24
|
+
# Bond, pointing away from a to c
|
|
25
|
+
AC_ = coords3d[c] - coords3d[a]
|
|
26
|
+
AC = AC_ / np.linalg.norm(AC_)
|
|
27
|
+
|
|
28
|
+
w = DummyImproper._get_cross_vec(coords3d, indices).flatten()
|
|
29
|
+
dummy_vec = (np.eye(3) - np.outer(AC, AC)) @ w
|
|
30
|
+
dummy_coords = B + r * dummy_vec
|
|
31
|
+
return dummy_coords
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def get_coords3d_and_indices_ext(coords3d, indices):
|
|
35
|
+
dummy_coords = DummyImproper._get_dummy_coords(coords3d, indices)
|
|
36
|
+
dummy_ind = len(coords3d)
|
|
37
|
+
coords3d_ext = np.zeros((len(coords3d) + 1, 3))
|
|
38
|
+
coords3d_ext[:dummy_ind] = coords3d
|
|
39
|
+
coords3d_ext[dummy_ind] = dummy_coords
|
|
40
|
+
a, b, c = indices
|
|
41
|
+
indices_ext = a, b, dummy_ind, c
|
|
42
|
+
return coords3d_ext, indices_ext
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def _weight(*args, **kwargs):
|
|
46
|
+
return 1
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def _calculate(coords3d, indices, gradient=False, fix_inner=False):
|
|
50
|
+
coords3d_ext, indices_ext = DummyImproper.get_coords3d_and_indices_ext(
|
|
51
|
+
coords3d, indices
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
results = Torsion._calculate(coords3d_ext, indices_ext, gradient=gradient)
|
|
55
|
+
if gradient:
|
|
56
|
+
val, grad = results
|
|
57
|
+
|
|
58
|
+
# import pdb; pdb.set_trace() # fmt: skip
|
|
59
|
+
# Remove entries that belong to the dummy atom.
|
|
60
|
+
grad = grad[:-3] # .reshape[](-1, 3)[[0, 1, 3]]
|
|
61
|
+
|
|
62
|
+
# Zero out contributions of the inner two atoms in the torsion.
|
|
63
|
+
# So basically only the atom at the first index moves.
|
|
64
|
+
#
|
|
65
|
+
# This usually degrades optimization convergence.
|
|
66
|
+
# if fix_inner:
|
|
67
|
+
# grad.reshape(-1, 3)[indices_ext[1:3]] = 0.0
|
|
68
|
+
return val, grad.flatten()
|
|
69
|
+
else:
|
|
70
|
+
return results
|