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.
Files changed (372) hide show
  1. hessian_ff/__init__.py +50 -0
  2. hessian_ff/analytical_hessian.py +609 -0
  3. hessian_ff/constants.py +46 -0
  4. hessian_ff/forcefield.py +339 -0
  5. hessian_ff/loaders.py +608 -0
  6. hessian_ff/native/Makefile +8 -0
  7. hessian_ff/native/__init__.py +28 -0
  8. hessian_ff/native/analytical_hessian.py +88 -0
  9. hessian_ff/native/analytical_hessian_ext.cpp +258 -0
  10. hessian_ff/native/bonded.py +82 -0
  11. hessian_ff/native/bonded_ext.cpp +640 -0
  12. hessian_ff/native/loader.py +349 -0
  13. hessian_ff/native/nonbonded.py +118 -0
  14. hessian_ff/native/nonbonded_ext.cpp +1150 -0
  15. hessian_ff/prmtop_parmed.py +23 -0
  16. hessian_ff/system.py +107 -0
  17. hessian_ff/terms/__init__.py +14 -0
  18. hessian_ff/terms/angle.py +73 -0
  19. hessian_ff/terms/bond.py +44 -0
  20. hessian_ff/terms/cmap.py +406 -0
  21. hessian_ff/terms/dihedral.py +141 -0
  22. hessian_ff/terms/nonbonded.py +209 -0
  23. hessian_ff/tests/__init__.py +0 -0
  24. hessian_ff/tests/conftest.py +75 -0
  25. hessian_ff/tests/data/small/complex.parm7 +1346 -0
  26. hessian_ff/tests/data/small/complex.pdb +125 -0
  27. hessian_ff/tests/data/small/complex.rst7 +63 -0
  28. hessian_ff/tests/test_coords_input.py +44 -0
  29. hessian_ff/tests/test_energy_force.py +49 -0
  30. hessian_ff/tests/test_hessian.py +137 -0
  31. hessian_ff/tests/test_smoke.py +18 -0
  32. hessian_ff/tests/test_validation.py +40 -0
  33. hessian_ff/workflows.py +889 -0
  34. mlmm/__init__.py +36 -0
  35. mlmm/__main__.py +7 -0
  36. mlmm/_version.py +34 -0
  37. mlmm/add_elem_info.py +374 -0
  38. mlmm/advanced_help.py +91 -0
  39. mlmm/align_freeze_atoms.py +601 -0
  40. mlmm/all.py +3535 -0
  41. mlmm/bond_changes.py +231 -0
  42. mlmm/bool_compat.py +223 -0
  43. mlmm/cli.py +574 -0
  44. mlmm/cli_utils.py +166 -0
  45. mlmm/default_group.py +337 -0
  46. mlmm/defaults.py +467 -0
  47. mlmm/define_layer.py +526 -0
  48. mlmm/dft.py +1041 -0
  49. mlmm/energy_diagram.py +253 -0
  50. mlmm/extract.py +2213 -0
  51. mlmm/fix_altloc.py +464 -0
  52. mlmm/freq.py +1406 -0
  53. mlmm/harmonic_constraints.py +140 -0
  54. mlmm/hessian_cache.py +44 -0
  55. mlmm/hessian_calc.py +174 -0
  56. mlmm/irc.py +638 -0
  57. mlmm/mlmm_calc.py +2262 -0
  58. mlmm/mm_parm.py +945 -0
  59. mlmm/oniom_export.py +1983 -0
  60. mlmm/oniom_import.py +457 -0
  61. mlmm/opt.py +1742 -0
  62. mlmm/path_opt.py +1353 -0
  63. mlmm/path_search.py +2299 -0
  64. mlmm/preflight.py +88 -0
  65. mlmm/py.typed +1 -0
  66. mlmm/pysis_runner.py +45 -0
  67. mlmm/scan.py +1047 -0
  68. mlmm/scan2d.py +1226 -0
  69. mlmm/scan3d.py +1265 -0
  70. mlmm/scan_common.py +184 -0
  71. mlmm/summary_log.py +736 -0
  72. mlmm/trj2fig.py +448 -0
  73. mlmm/tsopt.py +2871 -0
  74. mlmm/utils.py +2309 -0
  75. mlmm/xtb_embedcharge_correction.py +475 -0
  76. mlmm_toolkit-0.2.2.dev0.dist-info/METADATA +1159 -0
  77. mlmm_toolkit-0.2.2.dev0.dist-info/RECORD +372 -0
  78. mlmm_toolkit-0.2.2.dev0.dist-info/WHEEL +5 -0
  79. mlmm_toolkit-0.2.2.dev0.dist-info/entry_points.txt +2 -0
  80. mlmm_toolkit-0.2.2.dev0.dist-info/licenses/LICENSE +674 -0
  81. mlmm_toolkit-0.2.2.dev0.dist-info/top_level.txt +4 -0
  82. pysisyphus/Geometry.py +1667 -0
  83. pysisyphus/LICENSE +674 -0
  84. pysisyphus/TableFormatter.py +63 -0
  85. pysisyphus/TablePrinter.py +74 -0
  86. pysisyphus/__init__.py +12 -0
  87. pysisyphus/calculators/AFIR.py +452 -0
  88. pysisyphus/calculators/AnaPot.py +20 -0
  89. pysisyphus/calculators/AnaPot2.py +48 -0
  90. pysisyphus/calculators/AnaPot3.py +12 -0
  91. pysisyphus/calculators/AnaPot4.py +20 -0
  92. pysisyphus/calculators/AnaPotBase.py +337 -0
  93. pysisyphus/calculators/AnaPotCBM.py +25 -0
  94. pysisyphus/calculators/AtomAtomTransTorque.py +154 -0
  95. pysisyphus/calculators/CFOUR.py +250 -0
  96. pysisyphus/calculators/Calculator.py +844 -0
  97. pysisyphus/calculators/CerjanMiller.py +24 -0
  98. pysisyphus/calculators/Composite.py +123 -0
  99. pysisyphus/calculators/ConicalIntersection.py +171 -0
  100. pysisyphus/calculators/DFTBp.py +430 -0
  101. pysisyphus/calculators/DFTD3.py +66 -0
  102. pysisyphus/calculators/DFTD4.py +84 -0
  103. pysisyphus/calculators/Dalton.py +61 -0
  104. pysisyphus/calculators/Dimer.py +681 -0
  105. pysisyphus/calculators/Dummy.py +20 -0
  106. pysisyphus/calculators/EGO.py +76 -0
  107. pysisyphus/calculators/EnergyMin.py +224 -0
  108. pysisyphus/calculators/ExternalPotential.py +264 -0
  109. pysisyphus/calculators/FakeASE.py +35 -0
  110. pysisyphus/calculators/FourWellAnaPot.py +28 -0
  111. pysisyphus/calculators/FreeEndNEBPot.py +39 -0
  112. pysisyphus/calculators/Gaussian09.py +18 -0
  113. pysisyphus/calculators/Gaussian16.py +726 -0
  114. pysisyphus/calculators/HardSphere.py +159 -0
  115. pysisyphus/calculators/IDPPCalculator.py +49 -0
  116. pysisyphus/calculators/IPIClient.py +133 -0
  117. pysisyphus/calculators/IPIServer.py +234 -0
  118. pysisyphus/calculators/LEPSBase.py +24 -0
  119. pysisyphus/calculators/LEPSExpr.py +139 -0
  120. pysisyphus/calculators/LennardJones.py +80 -0
  121. pysisyphus/calculators/MOPAC.py +219 -0
  122. pysisyphus/calculators/MullerBrownSympyPot.py +51 -0
  123. pysisyphus/calculators/MultiCalc.py +85 -0
  124. pysisyphus/calculators/NFK.py +45 -0
  125. pysisyphus/calculators/OBabel.py +87 -0
  126. pysisyphus/calculators/ONIOMv2.py +1129 -0
  127. pysisyphus/calculators/ORCA.py +893 -0
  128. pysisyphus/calculators/ORCA5.py +6 -0
  129. pysisyphus/calculators/OpenMM.py +88 -0
  130. pysisyphus/calculators/OpenMolcas.py +281 -0
  131. pysisyphus/calculators/OverlapCalculator.py +908 -0
  132. pysisyphus/calculators/Psi4.py +218 -0
  133. pysisyphus/calculators/PyPsi4.py +37 -0
  134. pysisyphus/calculators/PySCF.py +341 -0
  135. pysisyphus/calculators/PyXTB.py +73 -0
  136. pysisyphus/calculators/QCEngine.py +106 -0
  137. pysisyphus/calculators/Rastrigin.py +22 -0
  138. pysisyphus/calculators/Remote.py +76 -0
  139. pysisyphus/calculators/Rosenbrock.py +15 -0
  140. pysisyphus/calculators/SocketCalc.py +97 -0
  141. pysisyphus/calculators/TIP3P.py +111 -0
  142. pysisyphus/calculators/TransTorque.py +161 -0
  143. pysisyphus/calculators/Turbomole.py +965 -0
  144. pysisyphus/calculators/VRIPot.py +37 -0
  145. pysisyphus/calculators/WFOWrapper.py +333 -0
  146. pysisyphus/calculators/WFOWrapper2.py +341 -0
  147. pysisyphus/calculators/XTB.py +418 -0
  148. pysisyphus/calculators/__init__.py +81 -0
  149. pysisyphus/calculators/cosmo_data.py +139 -0
  150. pysisyphus/calculators/parser.py +150 -0
  151. pysisyphus/color.py +19 -0
  152. pysisyphus/config.py +133 -0
  153. pysisyphus/constants.py +65 -0
  154. pysisyphus/cos/AdaptiveNEB.py +230 -0
  155. pysisyphus/cos/ChainOfStates.py +725 -0
  156. pysisyphus/cos/FreeEndNEB.py +25 -0
  157. pysisyphus/cos/FreezingString.py +103 -0
  158. pysisyphus/cos/GrowingChainOfStates.py +71 -0
  159. pysisyphus/cos/GrowingNT.py +309 -0
  160. pysisyphus/cos/GrowingString.py +508 -0
  161. pysisyphus/cos/NEB.py +189 -0
  162. pysisyphus/cos/SimpleZTS.py +64 -0
  163. pysisyphus/cos/__init__.py +22 -0
  164. pysisyphus/cos/stiffness.py +199 -0
  165. pysisyphus/drivers/__init__.py +17 -0
  166. pysisyphus/drivers/afir.py +855 -0
  167. pysisyphus/drivers/barriers.py +271 -0
  168. pysisyphus/drivers/birkholz.py +138 -0
  169. pysisyphus/drivers/cluster.py +318 -0
  170. pysisyphus/drivers/diabatization.py +133 -0
  171. pysisyphus/drivers/merge.py +368 -0
  172. pysisyphus/drivers/merge_mol2.py +322 -0
  173. pysisyphus/drivers/opt.py +375 -0
  174. pysisyphus/drivers/perf.py +91 -0
  175. pysisyphus/drivers/pka.py +52 -0
  176. pysisyphus/drivers/precon_pos_rot.py +669 -0
  177. pysisyphus/drivers/rates.py +480 -0
  178. pysisyphus/drivers/replace.py +219 -0
  179. pysisyphus/drivers/scan.py +212 -0
  180. pysisyphus/drivers/spectrum.py +166 -0
  181. pysisyphus/drivers/thermo.py +31 -0
  182. pysisyphus/dynamics/Gaussian.py +103 -0
  183. pysisyphus/dynamics/__init__.py +20 -0
  184. pysisyphus/dynamics/colvars.py +136 -0
  185. pysisyphus/dynamics/driver.py +297 -0
  186. pysisyphus/dynamics/helpers.py +256 -0
  187. pysisyphus/dynamics/lincs.py +105 -0
  188. pysisyphus/dynamics/mdp.py +364 -0
  189. pysisyphus/dynamics/rattle.py +121 -0
  190. pysisyphus/dynamics/thermostats.py +128 -0
  191. pysisyphus/dynamics/wigner.py +266 -0
  192. pysisyphus/elem_data.py +3473 -0
  193. pysisyphus/exceptions.py +2 -0
  194. pysisyphus/filtertrj.py +69 -0
  195. pysisyphus/helpers.py +623 -0
  196. pysisyphus/helpers_pure.py +649 -0
  197. pysisyphus/init_logging.py +50 -0
  198. pysisyphus/intcoords/Bend.py +69 -0
  199. pysisyphus/intcoords/Bend2.py +25 -0
  200. pysisyphus/intcoords/BondedFragment.py +32 -0
  201. pysisyphus/intcoords/Cartesian.py +41 -0
  202. pysisyphus/intcoords/CartesianCoords.py +140 -0
  203. pysisyphus/intcoords/Coords.py +56 -0
  204. pysisyphus/intcoords/DLC.py +197 -0
  205. pysisyphus/intcoords/DistanceFunction.py +34 -0
  206. pysisyphus/intcoords/DummyImproper.py +70 -0
  207. pysisyphus/intcoords/DummyTorsion.py +72 -0
  208. pysisyphus/intcoords/LinearBend.py +105 -0
  209. pysisyphus/intcoords/LinearDisplacement.py +80 -0
  210. pysisyphus/intcoords/OutOfPlane.py +59 -0
  211. pysisyphus/intcoords/PrimTypes.py +286 -0
  212. pysisyphus/intcoords/Primitive.py +137 -0
  213. pysisyphus/intcoords/RedundantCoords.py +659 -0
  214. pysisyphus/intcoords/RobustTorsion.py +59 -0
  215. pysisyphus/intcoords/Rotation.py +147 -0
  216. pysisyphus/intcoords/Stretch.py +31 -0
  217. pysisyphus/intcoords/Torsion.py +101 -0
  218. pysisyphus/intcoords/Torsion2.py +25 -0
  219. pysisyphus/intcoords/Translation.py +45 -0
  220. pysisyphus/intcoords/__init__.py +61 -0
  221. pysisyphus/intcoords/augment_bonds.py +126 -0
  222. pysisyphus/intcoords/derivatives.py +10512 -0
  223. pysisyphus/intcoords/eval.py +80 -0
  224. pysisyphus/intcoords/exceptions.py +37 -0
  225. pysisyphus/intcoords/findiffs.py +48 -0
  226. pysisyphus/intcoords/generate_derivatives.py +414 -0
  227. pysisyphus/intcoords/helpers.py +235 -0
  228. pysisyphus/intcoords/logging_conf.py +10 -0
  229. pysisyphus/intcoords/mp_derivatives.py +10836 -0
  230. pysisyphus/intcoords/setup.py +962 -0
  231. pysisyphus/intcoords/setup_fast.py +176 -0
  232. pysisyphus/intcoords/update.py +272 -0
  233. pysisyphus/intcoords/valid.py +89 -0
  234. pysisyphus/interpolate/Geodesic.py +93 -0
  235. pysisyphus/interpolate/IDPP.py +55 -0
  236. pysisyphus/interpolate/Interpolator.py +116 -0
  237. pysisyphus/interpolate/LST.py +70 -0
  238. pysisyphus/interpolate/Redund.py +152 -0
  239. pysisyphus/interpolate/__init__.py +9 -0
  240. pysisyphus/interpolate/helpers.py +34 -0
  241. pysisyphus/io/__init__.py +22 -0
  242. pysisyphus/io/aomix.py +178 -0
  243. pysisyphus/io/cjson.py +24 -0
  244. pysisyphus/io/crd.py +101 -0
  245. pysisyphus/io/cube.py +220 -0
  246. pysisyphus/io/fchk.py +184 -0
  247. pysisyphus/io/hdf5.py +49 -0
  248. pysisyphus/io/hessian.py +72 -0
  249. pysisyphus/io/mol2.py +146 -0
  250. pysisyphus/io/molden.py +293 -0
  251. pysisyphus/io/orca.py +189 -0
  252. pysisyphus/io/pdb.py +269 -0
  253. pysisyphus/io/psf.py +79 -0
  254. pysisyphus/io/pubchem.py +31 -0
  255. pysisyphus/io/qcschema.py +34 -0
  256. pysisyphus/io/sdf.py +29 -0
  257. pysisyphus/io/xyz.py +61 -0
  258. pysisyphus/io/zmat.py +175 -0
  259. pysisyphus/irc/DWI.py +108 -0
  260. pysisyphus/irc/DampedVelocityVerlet.py +134 -0
  261. pysisyphus/irc/Euler.py +22 -0
  262. pysisyphus/irc/EulerPC.py +345 -0
  263. pysisyphus/irc/GonzalezSchlegel.py +187 -0
  264. pysisyphus/irc/IMKMod.py +164 -0
  265. pysisyphus/irc/IRC.py +878 -0
  266. pysisyphus/irc/IRCDummy.py +10 -0
  267. pysisyphus/irc/Instanton.py +307 -0
  268. pysisyphus/irc/LQA.py +53 -0
  269. pysisyphus/irc/ModeKill.py +136 -0
  270. pysisyphus/irc/ParamPlot.py +53 -0
  271. pysisyphus/irc/RK4.py +36 -0
  272. pysisyphus/irc/__init__.py +31 -0
  273. pysisyphus/irc/initial_displ.py +219 -0
  274. pysisyphus/linalg.py +411 -0
  275. pysisyphus/line_searches/Backtracking.py +88 -0
  276. pysisyphus/line_searches/HagerZhang.py +184 -0
  277. pysisyphus/line_searches/LineSearch.py +232 -0
  278. pysisyphus/line_searches/StrongWolfe.py +108 -0
  279. pysisyphus/line_searches/__init__.py +9 -0
  280. pysisyphus/line_searches/interpol.py +15 -0
  281. pysisyphus/modefollow/NormalMode.py +40 -0
  282. pysisyphus/modefollow/__init__.py +10 -0
  283. pysisyphus/modefollow/davidson.py +199 -0
  284. pysisyphus/modefollow/lanczos.py +95 -0
  285. pysisyphus/optimizers/BFGS.py +99 -0
  286. pysisyphus/optimizers/BacktrackingOptimizer.py +113 -0
  287. pysisyphus/optimizers/ConjugateGradient.py +98 -0
  288. pysisyphus/optimizers/CubicNewton.py +75 -0
  289. pysisyphus/optimizers/FIRE.py +113 -0
  290. pysisyphus/optimizers/HessianOptimizer.py +1176 -0
  291. pysisyphus/optimizers/LBFGS.py +228 -0
  292. pysisyphus/optimizers/LayerOpt.py +411 -0
  293. pysisyphus/optimizers/MicroOptimizer.py +169 -0
  294. pysisyphus/optimizers/NCOptimizer.py +90 -0
  295. pysisyphus/optimizers/Optimizer.py +1084 -0
  296. pysisyphus/optimizers/PreconLBFGS.py +260 -0
  297. pysisyphus/optimizers/PreconSteepestDescent.py +7 -0
  298. pysisyphus/optimizers/QuickMin.py +74 -0
  299. pysisyphus/optimizers/RFOptimizer.py +181 -0
  300. pysisyphus/optimizers/RSA.py +99 -0
  301. pysisyphus/optimizers/StabilizedQNMethod.py +248 -0
  302. pysisyphus/optimizers/SteepestDescent.py +23 -0
  303. pysisyphus/optimizers/StringOptimizer.py +173 -0
  304. pysisyphus/optimizers/__init__.py +41 -0
  305. pysisyphus/optimizers/closures.py +301 -0
  306. pysisyphus/optimizers/cls_map.py +58 -0
  307. pysisyphus/optimizers/exceptions.py +6 -0
  308. pysisyphus/optimizers/gdiis.py +280 -0
  309. pysisyphus/optimizers/guess_hessians.py +311 -0
  310. pysisyphus/optimizers/hessian_updates.py +355 -0
  311. pysisyphus/optimizers/poly_fit.py +285 -0
  312. pysisyphus/optimizers/precon.py +153 -0
  313. pysisyphus/optimizers/restrict_step.py +24 -0
  314. pysisyphus/pack.py +172 -0
  315. pysisyphus/peakdetect.py +948 -0
  316. pysisyphus/plot.py +1031 -0
  317. pysisyphus/run.py +2106 -0
  318. pysisyphus/socket_helper.py +74 -0
  319. pysisyphus/stocastic/FragmentKick.py +132 -0
  320. pysisyphus/stocastic/Kick.py +81 -0
  321. pysisyphus/stocastic/Pipeline.py +303 -0
  322. pysisyphus/stocastic/__init__.py +21 -0
  323. pysisyphus/stocastic/align.py +127 -0
  324. pysisyphus/testing.py +96 -0
  325. pysisyphus/thermo.py +156 -0
  326. pysisyphus/trj.py +824 -0
  327. pysisyphus/tsoptimizers/RSIRFOptimizer.py +56 -0
  328. pysisyphus/tsoptimizers/RSPRFOptimizer.py +182 -0
  329. pysisyphus/tsoptimizers/TRIM.py +59 -0
  330. pysisyphus/tsoptimizers/TSHessianOptimizer.py +463 -0
  331. pysisyphus/tsoptimizers/__init__.py +23 -0
  332. pysisyphus/wavefunction/Basis.py +239 -0
  333. pysisyphus/wavefunction/DIIS.py +76 -0
  334. pysisyphus/wavefunction/__init__.py +25 -0
  335. pysisyphus/wavefunction/build_ext.py +42 -0
  336. pysisyphus/wavefunction/cart2sph.py +190 -0
  337. pysisyphus/wavefunction/diabatization.py +304 -0
  338. pysisyphus/wavefunction/excited_states.py +435 -0
  339. pysisyphus/wavefunction/gen_ints.py +1811 -0
  340. pysisyphus/wavefunction/helpers.py +104 -0
  341. pysisyphus/wavefunction/ints/__init__.py +0 -0
  342. pysisyphus/wavefunction/ints/boys.py +193 -0
  343. pysisyphus/wavefunction/ints/boys_table_N_64_xasym_27.1_step_0.01.npy +0 -0
  344. pysisyphus/wavefunction/ints/cart_gto3d.py +176 -0
  345. pysisyphus/wavefunction/ints/coulomb3d.py +25928 -0
  346. pysisyphus/wavefunction/ints/diag_quadrupole3d.py +10036 -0
  347. pysisyphus/wavefunction/ints/dipole3d.py +8762 -0
  348. pysisyphus/wavefunction/ints/int2c2e3d.py +7198 -0
  349. pysisyphus/wavefunction/ints/int3c2e3d_sph.py +65040 -0
  350. pysisyphus/wavefunction/ints/kinetic3d.py +8240 -0
  351. pysisyphus/wavefunction/ints/ovlp3d.py +3777 -0
  352. pysisyphus/wavefunction/ints/quadrupole3d.py +15054 -0
  353. pysisyphus/wavefunction/ints/self_ovlp3d.py +198 -0
  354. pysisyphus/wavefunction/localization.py +458 -0
  355. pysisyphus/wavefunction/multipole.py +159 -0
  356. pysisyphus/wavefunction/normalization.py +36 -0
  357. pysisyphus/wavefunction/pop_analysis.py +134 -0
  358. pysisyphus/wavefunction/shells.py +1171 -0
  359. pysisyphus/wavefunction/wavefunction.py +504 -0
  360. pysisyphus/wrapper/__init__.py +11 -0
  361. pysisyphus/wrapper/exceptions.py +2 -0
  362. pysisyphus/wrapper/jmol.py +120 -0
  363. pysisyphus/wrapper/mwfn.py +169 -0
  364. pysisyphus/wrapper/packmol.py +71 -0
  365. pysisyphus/xyzloader.py +168 -0
  366. pysisyphus/yaml_mods.py +45 -0
  367. thermoanalysis/LICENSE +674 -0
  368. thermoanalysis/QCData.py +244 -0
  369. thermoanalysis/__init__.py +0 -0
  370. thermoanalysis/config.py +3 -0
  371. thermoanalysis/constants.py +20 -0
  372. thermoanalysis/thermo.py +1011 -0
@@ -0,0 +1,212 @@
1
+ import h5py
2
+ import numpy as np
3
+
4
+ from pysisyphus.drivers.opt import run_opt
5
+ from pysisyphus.helpers_pure import highlight_text
6
+ from pysisyphus.optimizers.RFOptimizer import RFOptimizer
7
+ from pysisyphus.TablePrinter import TablePrinter
8
+
9
+
10
+ def relaxed_scan(
11
+ geom,
12
+ calc_getter,
13
+ constrain_prims,
14
+ target_values,
15
+ title,
16
+ max_cycles=25,
17
+ trust_radius=0.5,
18
+ thresh=1e-2,
19
+ dump=True,
20
+ ):
21
+ """Relaxed scan, allowing fixing of multiple primitive internals."""
22
+
23
+ # Constrain desired primitives
24
+ copy_kwargs = {
25
+ "coord_type": "redund",
26
+ "coord_kwargs": {"constrain_prims": constrain_prims},
27
+ }
28
+ scan_geom = geom.copy(**copy_kwargs)
29
+ calc = calc_getter()
30
+ scan_geom.set_calculator(calc)
31
+
32
+ lengths_ = [len(inds) for prim_type, *inds in constrain_prims]
33
+ if all([True for l in lengths_ if l == 2]):
34
+ unit = "au"
35
+ elif all([True for l in lengths_ if l in (3, 4)]):
36
+ unit = "rad"
37
+ else:
38
+ unit = "au (rad)"
39
+
40
+ typed_prims = scan_geom.internal.typed_prims
41
+ constr_inds = [typed_prims.index(cp) for cp in constrain_prims]
42
+ constr_fmt = lambda arr: f"({np.array2string(arr, precision=4)}) {unit}"
43
+
44
+ def print_constraints():
45
+ print(f"Desired constraints: {constr_fmt(target_values)}")
46
+ print(f" Actual constraints: {constr_fmt(scan_geom.coords[constr_inds])}")
47
+
48
+ trj = ""
49
+ scan_cart_coords = [scan_geom.cart_coords.copy()]
50
+ scan_energies = [scan_geom.energy]
51
+ actual_values = [scan_geom.coords[constr_inds]]
52
+
53
+ for i in range(max_cycles):
54
+ print(highlight_text(f"Step {i}", level=1))
55
+ cur_diff = target_values - scan_geom.coords[constr_inds]
56
+ step = cur_diff
57
+ step_norm = np.linalg.norm(step)
58
+ print_constraints()
59
+ print(f" Difference: {step_norm:.6f} {unit}\n")
60
+ if step_norm <= thresh:
61
+ print(
62
+ f"Relaxed scan converged! Norm of proposed step <= {thresh:.4f} {unit}"
63
+ )
64
+ break
65
+ if step_norm > trust_radius:
66
+ step = trust_radius * step / step_norm
67
+ new_coords = scan_geom.coords.copy()
68
+ new_coords[constr_inds] += step
69
+ scan_geom.set_coords(new_coords, update_constraints=True)
70
+
71
+ prefix = f"{title}_step_{i}"
72
+ opt_kwargs = {
73
+ "prefix": prefix,
74
+ "h5_group_name": prefix,
75
+ "dump": dump,
76
+ }
77
+ opt = RFOptimizer(scan_geom, **opt_kwargs)
78
+ opt.run()
79
+
80
+ scan_cart_coords.append(scan_geom.cart_coords.copy())
81
+ scan_energies.append(scan_geom.energy)
82
+ actual_values.append(scan_geom.coords[constr_inds])
83
+ trj += scan_geom.as_xyz() + "\n"
84
+
85
+ scan_cart_coords = np.array(scan_cart_coords)
86
+ scan_energies = np.array(scan_energies)
87
+ actual_values = np.array(actual_values)
88
+
89
+ if dump:
90
+ trj_fn = f"{title}_scan_trj.xyz"
91
+ with open(trj_fn, "w") as handle:
92
+ handle.write(trj)
93
+ print(f"Dumped optimized geometries to '{trj_fn}'.")
94
+
95
+ group_name = f"scan_{title}"
96
+ # with h5py.File("scan.h5", "a") as handle:
97
+ # try:
98
+ # del handle[group_name]
99
+ # except KeyError:
100
+ # pass
101
+ # group = handle.create_group(group_name)
102
+ # group.create_dataset("energies", data=scan_energies)
103
+ # group.create_dataset("cart_coords", data=scan_cart_coords)
104
+ # group.create_dataset("target_values", data=target_values)
105
+ # group.create_dataset("actual_values", data=actual_values)
106
+ # dt = h5py.vlen_dtype(np.dtype("int32"))
107
+ # cp = np.array([(pt.value, *ind) for pt, *ind in constrain_prims])
108
+ # cp_dataset = group.create_dataset(
109
+ # "constrain_prims", (len(target_values),), dtype=dt
110
+ # )
111
+ # cp_dataset[:] = cp
112
+ return scan_geom, scan_cart_coords, scan_energies
113
+
114
+
115
+ def relaxed_1d_scan(
116
+ geom,
117
+ calc_getter,
118
+ constrain_prims,
119
+ start,
120
+ step_size,
121
+ steps,
122
+ opt_key,
123
+ opt_kwargs,
124
+ pref=None,
125
+ callback=None,
126
+ ):
127
+ assert geom.coord_type == "redund", "'coord_type: redund' is required!"
128
+ if pref is None:
129
+ pref = ""
130
+ else:
131
+ pref = f"{pref}_"
132
+
133
+ constr_prim = constrain_prims[0]
134
+ copy_kwargs = {
135
+ "coord_type": "redund",
136
+ "coord_kwargs": {
137
+ "constrain_prims": constrain_prims,
138
+ "recalc_B": True,
139
+ },
140
+ }
141
+ constr_geom = geom.copy(**copy_kwargs)
142
+ constr_ind = constr_geom.internal.get_index_of_typed_prim(constrain_prims[0])
143
+ calc = calc_getter()
144
+ # Always return same calculator, so orbitals can be reused etc.
145
+ def _calc_getter():
146
+ return calc
147
+
148
+ # Drop PrimType at index 0
149
+ unit = "au" if len(constr_prim[1:]) == 2 else "rad"
150
+
151
+ org_val = constr_geom.coords[constr_ind]
152
+ cur_val = start
153
+ end_val = start + step_size * steps
154
+ target_scan_vals = np.linspace(cur_val, end_val, steps + 1)
155
+ print(
156
+ f" Coordinate: {constr_prim}\n"
157
+ f"Original value: {org_val:.4f} {unit}\n"
158
+ f" Initial value: {cur_val:.4f} {unit}\n"
159
+ f" Final value: {end_val:.4f} {unit}\n"
160
+ f" Steps: {steps}+1\n"
161
+ f" Step size: {step_size:.4f} {unit}\n"
162
+ )
163
+ scan_vals = list()
164
+ scan_geoms = list()
165
+ scan_energies = list()
166
+ scan_xyzs = list()
167
+
168
+ for cycle, cur_val in enumerate(target_scan_vals):
169
+ opt_kwargs_ = opt_kwargs.copy()
170
+ name = f"{pref}relaxed_scan_{cycle:04d}"
171
+ opt_kwargs_["prefix"] = name
172
+ opt_kwargs_["h5_group_name"] = name
173
+ # Keep a copy
174
+ scan_geoms.append(constr_geom.copy())
175
+
176
+ # Update constrained coordinate
177
+ new_coords = constr_geom.coords
178
+ new_coords[constr_ind] = cur_val
179
+ constr_geom.set_coords(new_coords, update_constraints=True)
180
+
181
+ title = f"{pref}Step {cycle:02d}, coord={cur_val:.4f} {unit}"
182
+ opt_result = run_opt(
183
+ constr_geom, _calc_getter, opt_key, opt_kwargs_, title=title, level=1
184
+ )
185
+ if callback is not None:
186
+ callback(opt_result)
187
+ scan_xyzs.append(constr_geom.as_xyz())
188
+ scan_vals.append(constr_geom.coords[constr_ind])
189
+ scan_energies.append(constr_geom.energy)
190
+
191
+ if not opt_result.opt.is_converged:
192
+ print(f"Step {cycle} did not converge. Breaking!")
193
+ break
194
+
195
+ with open(f"{pref}relaxed_scan_trj.xyz", "w") as handle:
196
+ handle.write("\n".join(scan_xyzs))
197
+
198
+ scan_data = np.stack((scan_vals, scan_energies), axis=1)
199
+ np.savetxt(f"{pref}relaxed_scan.dat", scan_data)
200
+ scan_vals, scan_energies = scan_data.T
201
+
202
+ print(highlight_text(f"Scan summary", level=1))
203
+ col_fmts = "int float float float float".split()
204
+ header = ("Step", f"Target / {unit}", f"Actual / {unit}", f"Δ / {unit}", "E / au")
205
+ table = TablePrinter(header, col_fmts, width=14)
206
+ table.print_header()
207
+ for step, (target, act, en) in enumerate(
208
+ zip(target_scan_vals, scan_vals, scan_energies)
209
+ ):
210
+ delta = target - act
211
+ table.print_row((step, target, act, delta, en))
212
+ return scan_geoms, scan_vals, scan_energies
@@ -0,0 +1,166 @@
1
+ # [1] https://gaussian.com/uvvisplot/
2
+
3
+ import dataclasses
4
+ from pathlib import Path
5
+ from typing import Tuple
6
+
7
+ import matplotlib.pyplot as plt
8
+ import numpy as np
9
+ from numpy.typing import NDArray
10
+
11
+ from pysisyphus.constants import AU2J, C, M_E, NA, PLANCK, AU2EV
12
+
13
+
14
+ # Computation of prefactor from Gaussian whitepaper
15
+ Q_E_ESU = 4.803204e-10 # Crazy unit, cm**1.5 g**0.5 s⁻¹
16
+ C_CM = C * 100 # Speed of light in cm/s
17
+ M_E_G = M_E * 1000 # Electron mass in gram
18
+ NM2CM = 1e7 # Nanometer to centimeter
19
+ PREFACTOR = ( # Prefactor in eq. (5) of [1]
20
+ np.sqrt(np.pi) * NA * Q_E_ESU**2 / (1e3 * np.log(10) * C_CM**2 * M_E_G)
21
+ ) / NM2CM
22
+ _04EV = 0.4 / AU2EV # in Hartree
23
+ # Factor used in converting energy in Hartree to wavelength
24
+ _AU2NM = PLANCK * C * 1e9 / AU2J
25
+
26
+
27
+ @dataclasses.dataclass
28
+ class Spectrum:
29
+ exc_ens: NDArray[float]
30
+ exc_ens_nm: NDArray[float]
31
+ fosc: NDArray[float]
32
+ nm: NDArray[float]
33
+ epsilon: NDArray[float]
34
+
35
+ def plot(self, **kwargs):
36
+ plot_spectrum(self.nm, self.epsilon, self.exc_ens_nm, self.fosc, **kwargs)
37
+
38
+ def save(self, fn):
39
+ act_fn = Path(fn).with_suffix(".npz")
40
+ data = dataclasses.asdict(self)
41
+ np.savez(act_fn, **data)
42
+ return act_fn
43
+
44
+ @staticmethod
45
+ def load(fn):
46
+ handle = np.load(fn)
47
+ kwargs = {key: handle[key] for key in handle.files}
48
+ return Spectrum(**kwargs)
49
+
50
+ @staticmethod
51
+ def from_orca(wf_fn, cis_fn, log_fn, **kwargs):
52
+ from pysisyphus.calculators.ORCA import get_exc_ens_fosc
53
+
54
+ exc_ens, fosc = get_exc_ens_fosc(wf_fn, cis_fn, log_fn)
55
+ return spectrum_from_ens_fosc(exc_ens, fosc, **kwargs)
56
+
57
+
58
+ def au2nm(au):
59
+ return _AU2NM / au
60
+
61
+
62
+ def get_grid(resolution, exc_ens, padding, min_, max_):
63
+ from_ = min(min_, int(exc_ens.min() - padding))
64
+ to_ = max(max_, int(exc_ens.max() + padding))
65
+ return np.arange(from_, to_ + resolution, step=resolution)
66
+
67
+
68
+ def homogeneous_broadening(
69
+ exc_ens: NDArray[float],
70
+ osc_strengths: NDArray[float],
71
+ resolution: float = 0.5,
72
+ stddev: float = _04EV,
73
+ from_to=None,
74
+ ) -> Tuple[NDArray[float], NDArray[float]]:
75
+ """Homogeneous broadening of stick spectra as outlined in Gaussian
76
+ whitepaper [1].
77
+
78
+ σ = 0.0147 au corresponds to about 0.4 eV. The function yields molar
79
+ extinction coefficients in l mol cm⁻¹."""
80
+ exc_ens_nm = au2nm(exc_ens)
81
+ if from_to is None:
82
+ from_to = np.array((exc_ens_nm[0], exc_ens_nm[-1]))
83
+ nm = get_grid(resolution, from_to, padding=100, min_=300, max_=900)
84
+ stddev_nm = au2nm(stddev)
85
+
86
+ quot = stddev_nm * (1 / nm[None, :] - (1 / exc_ens_nm[:, None]))
87
+ exp_ = np.exp(-(quot**2))
88
+ epsilon = PREFACTOR * osc_strengths[:, None] * stddev_nm * exp_
89
+ epsilon = epsilon.sum(axis=0)
90
+ return nm, epsilon
91
+
92
+
93
+ def spectrum_from_ens_fosc(exc_ens, fosc, **kwargs):
94
+ exc_ens_nm = au2nm(exc_ens)
95
+ nm, epsilon = homogeneous_broadening(exc_ens, fosc, **kwargs)
96
+ spectrum = Spectrum(
97
+ exc_ens=exc_ens,
98
+ exc_ens_nm=exc_ens_nm,
99
+ fosc=fosc,
100
+ nm=nm,
101
+ epsilon=epsilon,
102
+ )
103
+ return spectrum
104
+
105
+
106
+ def homogeneous_broadening_eV(
107
+ exc_ens: NDArray[float],
108
+ osc_strengths: NDArray[float],
109
+ resolution: float = 0.05,
110
+ stddev: float = 0.4,
111
+ from_to=None,
112
+ ) -> NDArray[float]:
113
+ """
114
+ Homogeneous broadening of stick spectra as outlined in eq. (5) and (6)
115
+ of https://doi.org/10.1063/1.4948471.
116
+
117
+ Parameters
118
+ ----------
119
+ exc_ens
120
+ Excitation energies in Hartree.
121
+ osc_strengths
122
+ Unitless oscillator strengths.
123
+ resolution
124
+ Resolution of broadened spectra in eV.
125
+ stddev
126
+ Standard deviation, sigma in eq. (6).
127
+ from_to
128
+ Limits for abscissa of spectrum in eV.
129
+
130
+ Returns
131
+ -------
132
+ eV
133
+ Spectral grid in eV.
134
+ epsilon
135
+ Molar extinction coefficient.
136
+ """
137
+ exc_ens_eV = exc_ens * AU2EV
138
+ if from_to is None:
139
+ from_to = np.array((exc_ens_eV[0], exc_ens_eV[-1]))
140
+ eV = get_grid(resolution, from_to, padding=2, min_=0, max_=5)
141
+ epsilon = (osc_strengths / (3.7922e33 * 4 * 2.926e-39 * np.sqrt(np.pi) * stddev))[
142
+ None, :
143
+ ] * np.exp(-(((eV[None, :] - exc_ens_eV) / stddev) ** 2))
144
+ epsilon = epsilon.sum(axis=0)
145
+ return eV, epsilon
146
+
147
+
148
+ def plot_spectrum(nm, epsilon, exc_ens_nm=None, fosc=None, show=False):
149
+ fig, ax = plt.subplots()
150
+ ax.plot(nm, epsilon)
151
+ ax.set_ylabel(r"$\epsilon$")
152
+ ax.set_xlabel(r"$\lambda$ / nm")
153
+ axs = [
154
+ ax,
155
+ ]
156
+
157
+ if (exc_ens_nm is not None) and (fosc is not None):
158
+ ax2 = ax.twinx()
159
+ ax2.stem(exc_ens_nm, fosc, "r", markerfmt=" ", basefmt=" ")
160
+ ax2.set_ylabel("fosc")
161
+ axs.append(ax2)
162
+ fig.tight_layout()
163
+ if show:
164
+ plt.show()
165
+
166
+ return fig, axs
@@ -0,0 +1,31 @@
1
+ import argparse
2
+ import sys
3
+
4
+ from pysisyphus.config import p_DEFAULT, T_DEFAULT
5
+ from pysisyphus.thermo import get_thermoanalysis_from_hess_h5, print_thermoanalysis
6
+
7
+
8
+ def parse_args(args):
9
+ parser = argparse.ArgumentParser()
10
+ parser.add_argument("hess_h5", help="HDF5 Hessian file from pysisyphus.")
11
+ parser.add_argument("-T", default=T_DEFAULT, type=float, help="Temperature")
12
+ parser.add_argument("-p", default=p_DEFAULT, type=float, help="Pressure")
13
+ parser.add_argument("--pg", default="c1", help="Point group.")
14
+ parser.add_argument(
15
+ "--calorie",
16
+ action="store_true",
17
+ help="Output in kcal mol⁻¹ instead of kJ mol⁻¹.",
18
+ )
19
+
20
+ return parser.parse_args(args)
21
+
22
+
23
+ def run_thermo():
24
+ args = parse_args(sys.argv[1:])
25
+ hess_h5 = args.hess_h5
26
+ T = args.T
27
+ p = args.p
28
+ point_group = args.pg
29
+ unit = "calorie" if args.calorie else "joule"
30
+ thermo = get_thermoanalysis_from_hess_h5(hess_h5, T=T, p=p, point_group=point_group)
31
+ print_thermoanalysis(thermo, unit=unit)
@@ -0,0 +1,103 @@
1
+ import numpy as np
2
+
3
+
4
+ class DummyColvar:
5
+ def eval(self, x):
6
+ assert np.isscalar(
7
+ x
8
+ ), f"DummyColvar can only be used for scalar inputs, but got {x}!"
9
+ return x, 1
10
+
11
+
12
+ class Gaussian:
13
+ def __init__(self, w=1, s=1, x0=None, colvar=None, dump_name=None):
14
+ """
15
+ See:
16
+ https://doi.org/10.1016/j.cpc.2018.02.017
17
+
18
+ V(f(x)) = w * exp(-(f(x) - f0)**2 / (2*s**2))
19
+
20
+ F(x) = -dV/dx = -dV/df * df/dx
21
+
22
+ See also:
23
+ https://doi.org/10.1103/PhysRevLett.90.238302
24
+ """
25
+ # Gaussian height w and standard deviation s are read-only properties.
26
+ # This way they can't be accidentally altered, which would invalidate the
27
+ # values precomputed below.
28
+ self._w = w
29
+ self._s = s
30
+ if x0 is None:
31
+ x0 = np.array(())
32
+ self.x0 = np.ravel(x0)
33
+
34
+ # Collective variable
35
+ if colvar is None:
36
+ colvar = DummyColvar()
37
+ self.colvar = colvar
38
+ self.dump_name = dump_name
39
+ if self.dump_name:
40
+ self.dump_fn = (
41
+ dump_name
42
+ if self.dump_name.endswith(".gau")
43
+ else f"{self.dump_name}.gau"
44
+ )
45
+ else:
46
+ self.dump_fn = None
47
+
48
+ # Reset file and write header
49
+ if self.dump_fn:
50
+ with open(self.dump_fn, "w") as handle:
51
+ handle.write(f"# {dump_name}\n# step s w center\n")
52
+
53
+ # Store some values, to avoid recalculation
54
+ self.s2 = self.s ** 2
55
+ self.one_over_2s2 = 1 / (2 * self.s2)
56
+ self.minus_w_over_s2 = -self.w / self.s2
57
+
58
+ @property
59
+ def w(self):
60
+ return self._w
61
+
62
+ @property
63
+ def s(self):
64
+ return self._s
65
+
66
+ def calculate(self, coords, x0=None, gradient=False):
67
+ """Return potential and gradient for Gaussian(s) centered at x0."""
68
+
69
+ if x0 is None:
70
+ x0 = self.x0
71
+ x, cr_grad = self.colvar.eval(coords)
72
+ # TODO: allow colvar to supply a callback, that overrides the difference
73
+ # below. This will be needed to support periodic colvars, like torsion.
74
+ # There we can't just calculate the naive difference.
75
+ diff = x - np.atleast_1d(x0)
76
+ exp_ = np.exp(-(diff ** 2) * self.one_over_2s2)
77
+ to_return = self.w * exp_.sum()
78
+
79
+ if gradient:
80
+ try:
81
+ grad = np.einsum("i,i,ijk->jk", diff, exp_, cr_grad[None, :, :])
82
+ except TypeError:
83
+ grad = diff * exp_ * cr_grad
84
+
85
+ # Finalize & append gradient
86
+ grad *= self.minus_w_over_s2
87
+ to_return = to_return, grad
88
+ return to_return
89
+
90
+ def value(self, coords, x0=None):
91
+ return self.calculate(coords, x0)
92
+
93
+ def gradient(self, coords, x0=None):
94
+ _, grad = self.calculate(coords, x0, gradient=True)
95
+ return grad
96
+
97
+ def eval(self, coords, x0=None):
98
+ return self.calculate(coords, x0, gradient=True)
99
+
100
+ def dump(self, step, s, w, center):
101
+ line = f"{step:08d} {s:.12f} {w:.12f} {center:.12f}\n"
102
+ with open(self.dump_fn, "a") as handle:
103
+ handle.write(line)
@@ -0,0 +1,20 @@
1
+ import logging
2
+
3
+ from pysisyphus.dynamics.colvars import get_colvar
4
+ from pysisyphus.dynamics.Gaussian import Gaussian
5
+ from pysisyphus.dynamics.helpers import get_mb_velocities_for_geom
6
+ from pysisyphus.dynamics.mdp import mdp
7
+ from pysisyphus.dynamics.rattle import rattle_closure
8
+ from pysisyphus.dynamics.driver import md
9
+ from pysisyphus.dynamics.wigner import get_wigner_sampler
10
+
11
+
12
+ logger = logging.getLogger("dynamics")
13
+ logger.setLevel(logging.DEBUG)
14
+ # delay = True prevents creation of empty logfiles
15
+ handler = logging.FileHandler("dynamics.log", mode="w", delay=True)
16
+ # fmt_str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
17
+ fmt_str = "%(asctime)s - %(message)s"
18
+ formatter = logging.Formatter(fmt_str)
19
+ handler.setFormatter(formatter)
20
+ logger.addHandler(handler)
@@ -0,0 +1,136 @@
1
+ import abc
2
+ import types
3
+
4
+ import numpy as np
5
+ import autograd
6
+ import autograd.numpy as anp
7
+
8
+ from pysisyphus.intcoords.derivatives import dq_b, dq_a
9
+
10
+
11
+ class Colvar(metaclass=abc.ABCMeta):
12
+ def __init__(self, force_agrad=False):
13
+ try:
14
+ getattr(self, "_gradient")
15
+ except AttributeError:
16
+ force_agrad = True
17
+
18
+ # Set autograd gradient method, if no _gradient is not implemented
19
+ # a autograd is forced.
20
+ if force_agrad:
21
+ grad_func = autograd.grad(self.value)
22
+
23
+ def wrapped(self, c3d):
24
+ return grad_func(c3d)
25
+
26
+ self._gradient = types.MethodType(wrapped, self)
27
+ # Store a flag to indicate use of autograd
28
+ self.agrad = force_agrad
29
+
30
+ @abc.abstractmethod
31
+ def value(self, c3d):
32
+ pass
33
+
34
+ def gradient(self, c3d):
35
+ return self._gradient(c3d)
36
+
37
+ def eval(self, coords):
38
+ c3d = coords.reshape(-1, 3)
39
+ return self.value(c3d), self.gradient(c3d)
40
+
41
+ def _wilson_gradient(self, func, c3d):
42
+ """Gradient of primitive internal w.r.t. Cartesians."""
43
+ grad = np.zeros_like(c3d)
44
+ grad[self.indices] = func(*c3d[self.indices].flatten()).reshape(-1, 3)
45
+ return grad
46
+
47
+ def __str__(self):
48
+ if hasattr(self, "indices"):
49
+ str_ = f"{self.__class__.__name__}({self.indices})"
50
+ else:
51
+ str_ = super().__str__()
52
+ return str_
53
+
54
+
55
+ class CVDistance(Colvar):
56
+ def __init__(self, indices, **kwargs):
57
+ self.indices = list(indices)
58
+ self.i, self.j = self.indices
59
+ super().__init__(**kwargs)
60
+
61
+ def value(self, c3d):
62
+ return anp.linalg.norm(c3d[self.i] - c3d[self.j])
63
+
64
+ def _gradient(self, c3d): # lgtm [py/attribute-shadows-method]
65
+ return self._wilson_gradient(dq_b, c3d)
66
+
67
+
68
+ class CVBend(Colvar):
69
+ def __init__(self, indices, **kwargs):
70
+ self.indices = list(indices)
71
+ # Bonded like
72
+ # i---j <- central atom
73
+ # |
74
+ # k
75
+ self.i, self.j, self.k = self.indices
76
+ super().__init__(**kwargs)
77
+
78
+ def value(self, c3d):
79
+ u_dash = c3d[self.i] - c3d[self.j]
80
+ v_dash = c3d[self.k] - c3d[self.j]
81
+ u_norm = anp.linalg.norm(u_dash)
82
+ v_norm = anp.linalg.norm(v_dash)
83
+ u = u_dash / u_norm
84
+ v = v_dash / v_norm
85
+ rad = anp.arccos(anp.dot(u, v))
86
+ return rad
87
+
88
+ def _gradient(self, c3d): # lgtm [py/attribute-shadows-method]
89
+ return self._wilson_gradient(dq_a, c3d)
90
+
91
+
92
+ class CVTorsion(Colvar):
93
+ def __init__(self, indices, **kwargs):
94
+ self.indices = list(indices)
95
+ # Bonded like
96
+ # i--u--j
97
+ # |
98
+ # w
99
+ # |
100
+ # k--v--l
101
+ self.i, self.j, self.k, self.l = self.indices
102
+ super().__init__(**kwargs)
103
+
104
+ def value(self, c3d):
105
+ u_dash = c3d[self.i] - c3d[self.j]
106
+ v_dash = c3d[self.l] - c3d[self.k]
107
+ w_dash = c3d[self.k] - c3d[self.j]
108
+ u_norm = anp.linalg.norm(u_dash)
109
+ v_norm = anp.linalg.norm(v_dash)
110
+ w_norm = anp.linalg.norm(w_dash)
111
+ u = u_dash / u_norm
112
+ v = v_dash / v_norm
113
+ w = w_dash / w_norm
114
+ phi_u = anp.arccos(anp.dot(u, w))
115
+ phi_v = anp.arccos(-anp.dot(w, v))
116
+ uxw = anp.cross(u, w)
117
+ vxw = anp.cross(v, w)
118
+ cos_dihed = anp.dot(uxw, vxw)/(anp.sin(phi_u)*anp.sin(phi_v))
119
+ # Restrict cos_dihed to the allowed interval for arccos [-1, 1]
120
+ cos_dihed = min(1, max(cos_dihed, -1))
121
+
122
+ rad = anp.arccos(cos_dihed)
123
+ if (rad != np.pi) and (anp.dot(vxw, u) < 0):
124
+ rad *= -1
125
+ return rad
126
+
127
+
128
+ COLVARS = {
129
+ "distance": CVDistance,
130
+ "bend": CVBend,
131
+ "torsion": CVTorsion,
132
+ }
133
+
134
+
135
+ def get_colvar(key, kwargs):
136
+ return COLVARS[key](**kwargs)