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,659 @@
1
+ # [1] https://doi.org/10.1063/1.1515483 optimization review
2
+ # [2] https://doi.org/10.1063/1.471864 delocalized internal coordinates
3
+ # [3] https://doi.org/10.1016/0009-2614(95)00646-L lindh model hessian
4
+ # [4] 10.1002/(SICI)1096-987X(19990730)20:10<1067::AID-JCC9>3.0.CO;2-V
5
+ # Handling of corner cases
6
+ # [5] https://doi.org/10.1063/1.462844 , Pulay 1992
7
+
8
+ import itertools as it
9
+ import math
10
+ from operator import itemgetter
11
+
12
+ import numpy as np
13
+ import torch
14
+
15
+ from pysisyphus.config import (
16
+ BEND_MIN_DEG,
17
+ LB_MIN_DEG,
18
+ DIHED_MAX_DEG,
19
+ )
20
+ from pysisyphus.elem_data import get_tm_indices
21
+ from pysisyphus.linalg import svd_inv
22
+ from pysisyphus.intcoords.exceptions import PrimitiveNotDefinedException
23
+ from pysisyphus.intcoords.update import transform_int_step
24
+ from pysisyphus.intcoords.eval import (
25
+ eval_primitives,
26
+ check_primitives,
27
+ )
28
+
29
+ from pysisyphus.intcoords.logging_conf import logger
30
+ from pysisyphus.intcoords.PrimTypes import (
31
+ normalize_prim_inputs,
32
+ PrimTypes,
33
+ # PrimType classes
34
+ Bonds,
35
+ Bends,
36
+ DummyCoords,
37
+ LinearBends,
38
+ Cartesians,
39
+ Dihedrals,
40
+ OutOfPlanes,
41
+ Rotations,
42
+ Translations,
43
+ )
44
+
45
+ from pysisyphus.intcoords.setup import (
46
+ setup_redundant,
47
+ get_primitives,
48
+ )
49
+ from pysisyphus.intcoords.valid import check_typed_prims
50
+
51
+
52
+ class RedundantCoords:
53
+ def __init__(
54
+ self,
55
+ atoms,
56
+ coords3d,
57
+ masses=None,
58
+ bond_factor=1.3,
59
+ typed_prims=None,
60
+ define_prims=None,
61
+ constrain_prims=None,
62
+ freeze_atoms=None,
63
+ freeze_atoms_exclude=False,
64
+ internals_with_frozen=False,
65
+ define_for=None,
66
+ bonds_only=False,
67
+ check_bends=True,
68
+ rebuild=True,
69
+ bend_min_deg=BEND_MIN_DEG,
70
+ dihed_max_deg=DIHED_MAX_DEG,
71
+ lb_min_deg=LB_MIN_DEG,
72
+ weighted=False,
73
+ min_weight=0.3,
74
+ # Corresponds to a threshold of 1e-7 for eigenvalues of G, as proposed by
75
+ # Pulay in [5].
76
+ svd_inv_thresh=3.16e-4,
77
+ recalc_B=False,
78
+ tric=False,
79
+ hybrid=False,
80
+ hbond_angles=False,
81
+ rm_for_frag=None,
82
+ ):
83
+ self.atoms = atoms
84
+ self.coords3d = np.reshape(coords3d, (-1, 3)).copy()
85
+ self.masses = masses
86
+ self.bond_factor = bond_factor
87
+ if typed_prims is not None:
88
+ typed_prims = normalize_prim_inputs(typed_prims)
89
+ # Define additional primitives
90
+ if define_prims is None:
91
+ define_prims = list()
92
+ self.define_prims = normalize_prim_inputs(define_prims)
93
+ if freeze_atoms is None:
94
+ freeze_atoms = list()
95
+ self.freeze_atoms = np.array(freeze_atoms, dtype=int)
96
+ self.freeze_atoms_exclude = freeze_atoms_exclude
97
+ self.internals_with_frozen = internals_with_frozen
98
+ self.define_for = define_for
99
+ # Constrain primitives
100
+ if constrain_prims is None:
101
+ constrain_prims = list()
102
+ self.constrain_prims = normalize_prim_inputs(constrain_prims)
103
+ self.bonds_only = bonds_only
104
+ self.check_bends = check_bends
105
+ self.rebuild = rebuild
106
+ self.bend_min_deg = bend_min_deg
107
+ self.dihed_max_deg = dihed_max_deg
108
+ self.lb_min_deg = lb_min_deg
109
+ self.weighted = weighted
110
+ self.min_weight = float(min_weight)
111
+ assert self.min_weight > 0.0, "min_weight must be a positive rational!"
112
+ self.svd_inv_thresh = svd_inv_thresh
113
+ self.recalc_B = recalc_B
114
+ self.tric = tric
115
+ self.hybrid = hybrid
116
+ self.hbond_angles = hbond_angles
117
+ self.rm_for_frag = rm_for_frag
118
+
119
+ self._B_prim = None
120
+ # Lists for the other types of primitives will be created afterwards.
121
+ self.logger = logger
122
+
123
+ if self.weighted:
124
+ self.log(
125
+ "Coordinate weighting requested, min_weight="
126
+ f"{self.min_weight:.2f}. Calculating bond factor."
127
+ )
128
+ # Screening function is
129
+ # ρ(d) = exp(-(d/sum_cov_rad - 1)
130
+ #
131
+ # Swart proposed a min_weight of ρ(d) = 0.3. With this we can
132
+ # calculate the appropriate factor for the bond detection.
133
+ # d = (1 - ln(0.3)) * sum_cov_rad
134
+ # bond_factor = (1 - ln(0.3)) ≈ 2.204
135
+ #
136
+ # The snippet below prints weights and corresponding bond_factors.
137
+ # [f"{w:.2f}: {1-np.log(w):.4f}" for w in np.linspace(0.3, 1, 25)]
138
+ self.bond_factor = -math.log(self.min_weight) + 1
139
+ self.log(f"Using a factor of {self.bond_factor:.6f} for bond detection.")
140
+ self.log(f"Using svd_inv_thresh={self.svd_inv_thresh:.4e} for inversions.")
141
+
142
+ # Set up primitive coordinate indices
143
+ if typed_prims is None:
144
+ self.set_primitive_indices(
145
+ self.atoms,
146
+ self.coords3d,
147
+ )
148
+ # Use supplied typed_prims
149
+ else:
150
+ unique_typed_prims = set(typed_prims) | set(self.define_prims)
151
+ self.typed_prims = list(unique_typed_prims)
152
+
153
+ if self.bonds_only:
154
+ self.typed_prims = self.bond_typed_prims
155
+
156
+ self.primitives = get_primitives(
157
+ self.coords3d,
158
+ self.typed_prims,
159
+ logger=self.logger,
160
+ )
161
+
162
+ # First evaluation of internal coordinates
163
+ self._prim_internals = self.eval(self.coords3d)
164
+ self._prim_coords = np.array(
165
+ [prim_int.val for prim_int in self._prim_internals]
166
+ )
167
+ check_primitives(
168
+ self.coords3d, self.primitives, B=self.B_prim, logger=self.logger
169
+ )
170
+
171
+ ref_num = len(self.typed_prims)
172
+ if self.bonds_only:
173
+ ref_num = len(self.bond_indices)
174
+ assert len(self.primitives) == ref_num
175
+
176
+ self.backtransform_counter = 0
177
+
178
+ def set_inds_from_typed_prims(self, typed_prims):
179
+ # These lists will hold the index of the respective typed_prims
180
+ # in 'self.typed_prims'.
181
+ self._bond_inds = list()
182
+ self._bend_inds = list()
183
+ self._linear_bend_inds = list()
184
+ self._dihedral_inds = list()
185
+ self._rotation_inds = list()
186
+ self._translation_inds = list()
187
+ self._cartesian_inds = list()
188
+ self._outofplane_inds = list()
189
+ self._dummycoord_inds = list()
190
+ self._cartesian_inds = list()
191
+
192
+ self._bond_atom_inds = list()
193
+ self._bend_atom_inds = list()
194
+ self._dihedral_atom_inds = list()
195
+
196
+ self._bond_typed_prims = list()
197
+
198
+ for i, (pt, *indices) in enumerate(typed_prims):
199
+ if pt in Bonds:
200
+ append_to = self._bond_inds
201
+ self._bond_atom_inds.append(indices)
202
+ self._bond_typed_prims.append((pt, *indices))
203
+ elif pt in Bends:
204
+ append_to = self._bend_inds
205
+ self._bend_atom_inds.append(indices)
206
+ elif pt in LinearBends:
207
+ append_to = self._linear_bend_inds
208
+ elif pt in Dihedrals:
209
+ append_to = self._dihedral_inds
210
+ self._dihedral_atom_inds.append(indices)
211
+ elif pt in Rotations:
212
+ append_to = self._rotation_inds
213
+ elif pt in Translations:
214
+ append_to = self._translation_inds
215
+ elif pt in Cartesians:
216
+ append_to = self._cartesian_inds
217
+ elif pt in OutOfPlanes:
218
+ append_to = self._outofplane_inds
219
+ elif pt in DummyCoords:
220
+ append_to = self._dummycoord_inds
221
+ elif pt in Cartesians:
222
+ append_to = self._cartesian_inds
223
+ else:
224
+ raise Exception("Unhandled PrimType!")
225
+ append_to.append(i)
226
+
227
+ def log(self, message):
228
+ self.logger.debug(message)
229
+
230
+ def clear(self):
231
+ self._B_prim = None
232
+ self._prim_coords = None
233
+ self._prim_internals = None
234
+ self._P = None
235
+
236
+ @property
237
+ def coords3d(self):
238
+ return self._coords3d
239
+
240
+ @coords3d.setter
241
+ def coords3d(self, coords3d):
242
+ self._coords3d = coords3d.reshape(-1, 3)
243
+ self.clear()
244
+
245
+ @property
246
+ def typed_prims(self):
247
+ return self._typed_prims
248
+
249
+ @typed_prims.setter
250
+ def typed_prims(self, typed_prims):
251
+ self.log(f"Checking {len(typed_prims)} supplied typed primitives.")
252
+ valid_typed_prims = check_typed_prims(
253
+ self.coords3d,
254
+ typed_prims,
255
+ bend_min_deg=self.bend_min_deg,
256
+ dihed_max_deg=self.dihed_max_deg,
257
+ lb_min_deg=self.lb_min_deg,
258
+ check_bends=self.check_bends,
259
+ )
260
+
261
+ def tp_sort(tp):
262
+ pt, *indices = tp
263
+ key = pt
264
+ # We use the fact that list.sort is stable, that is elements that compare
265
+ # equal retain their order. So we assign PrimTypes.ROTATION to all rotations,
266
+ # to remain the ABC-order for each fragment. The same goes for the translations.
267
+ if pt in Rotations:
268
+ key = PrimTypes.ROTATION
269
+ elif pt in Translations:
270
+ key = PrimTypes.TRANSLATION
271
+ elif pt in Cartesians:
272
+ key = PrimTypes.CARTESIAN
273
+ return (key, *indices)
274
+
275
+ # Sort by PrimType
276
+ valid_typed_prims.sort(key=tp_sort)
277
+
278
+ self.log(
279
+ f"{len(valid_typed_prims)} primitives are valid at the current Cartesians."
280
+ )
281
+ if len(valid_typed_prims) != len(typed_prims):
282
+ self.log("Invalid primitives:")
283
+ for i, invalid_prim in enumerate(set(typed_prims) - set(valid_typed_prims)):
284
+ self.log(f"\t{i:02d}: {invalid_prim}")
285
+ self._typed_prims = valid_typed_prims
286
+ self.set_inds_from_typed_prims(self.typed_prims)
287
+
288
+ @property
289
+ def primitives(self):
290
+ return self._primitives
291
+
292
+ @primitives.setter
293
+ def primitives(self, primitives):
294
+ self._primitives = primitives
295
+
296
+ @property
297
+ def prim_indices_set(self):
298
+ return set([tuple(indices) for pt, *indices in self.typed_prims])
299
+
300
+ @property
301
+ def prim_internals(self):
302
+ if self._prim_internals is None:
303
+ self._prim_internals = self.eval(self.coords3d)
304
+ return self._prim_internals
305
+
306
+ @prim_internals.setter
307
+ def prim_internals(self, prim_internals):
308
+ self._prim_internals = prim_internals
309
+
310
+ @property
311
+ def prim_coords(self):
312
+ return np.array([prim_int.val for prim_int in self.prim_internals])
313
+
314
+ def return_inds(self, slice_):
315
+ return np.array([prim_int.indices for prim_int in self.prim_internals[slice_]])
316
+
317
+ def get_prim_internals_by_indices(self, indices):
318
+ if len(indices) == 0:
319
+ pis = []
320
+ elif len(indices) == 1:
321
+ pis = [self.prim_internals[indices[0]]]
322
+ else:
323
+ pis = itemgetter(*indices)(self.prim_internals)
324
+ return pis
325
+
326
+ @property
327
+ def bond_indices(self):
328
+ return self._bond_inds
329
+
330
+ @property
331
+ def bond_atom_indices(self):
332
+ return self._bond_atom_inds
333
+
334
+ @property
335
+ def bond_typed_prims(self):
336
+ return self._bond_typed_prims
337
+
338
+ @property
339
+ def bend_indices(self):
340
+ return self._bend_inds
341
+
342
+ @property
343
+ def bend_atom_indices(self):
344
+ return self._bend_atom_inds
345
+
346
+ @property
347
+ def linear_bend_indices(self):
348
+ return self._linear_bend_inds
349
+
350
+ @property
351
+ def dihedral_indices(self):
352
+ return self._dihedral_inds
353
+
354
+ @property
355
+ def dihedral_atom_indices(self):
356
+ return self._dihedral_atom_inds
357
+
358
+ @property
359
+ def rotation_indices(self):
360
+ return self._rotation_inds
361
+
362
+ @property
363
+ def translation_indices(self):
364
+ return self._translation_inds
365
+
366
+ @property
367
+ def cartesian_indices(self):
368
+ return self._cartesian_inds
369
+
370
+ @property
371
+ def outofplane_indices(self):
372
+ return self._outofplane_inds
373
+
374
+ @property
375
+ def coords(self):
376
+ return self.prim_coords
377
+
378
+ def get_index_of_typed_prim(self, typed_prim):
379
+ """Index in self.typed_prims for the supplied typed_prim."""
380
+ ref_len = len(typed_prim)
381
+ ref_inds = typed_prim[1:]
382
+ for i, tp in enumerate(self.typed_prims):
383
+ if (len(tp) != ref_len) or tp[0] != typed_prim[0]:
384
+ continue
385
+
386
+ if (tp[1:] == ref_inds) or (tp[1:] == ref_inds[::-1]):
387
+ return i
388
+ self.log(f"Typed primitive {typed_prim} is not defined!")
389
+ raise PrimitiveNotDefinedException(typed_prim)
390
+
391
+ @property
392
+ def B_prim(self):
393
+ """Wilson B-Matrix"""
394
+ if self._B_prim is None:
395
+ self._B_prim = np.array([prim_int.grad for prim_int in self.prim_internals])
396
+
397
+ return self._B_prim
398
+
399
+ @property
400
+ def B(self):
401
+ """Wilson B-Matrix"""
402
+ return self.B_prim
403
+
404
+ def inv_B(self, B):
405
+ return B.T.dot(svd_inv(B.dot(B.T), thresh=self.svd_inv_thresh, hermitian=True))
406
+
407
+ def inv_Bt(self, B):
408
+ return svd_inv(B.dot(B.T), thresh=self.svd_inv_thresh, hermitian=True).dot(B)
409
+
410
+ @property
411
+ def Bt_inv_prim(self):
412
+ """Transposed generalized inverse of the primitive Wilson B-Matrix."""
413
+ return self.inv_Bt(self.B_prim)
414
+
415
+ @property
416
+ def Bt_inv(self):
417
+ """Transposed generalized inverse of the Wilson B-Matrix."""
418
+ return self.inv_Bt(self.B)
419
+
420
+ @property
421
+ def B_inv_prim(self):
422
+ """Generalized inverse of the primitive Wilson B-Matrix."""
423
+ return self.inv_B(self.B_prim)
424
+
425
+ @property
426
+ def B_inv(self):
427
+ """Generalized inverse of the Wilson B-Matrix."""
428
+ return self.inv_B(self.B)
429
+
430
+ @property
431
+ def constrained_indices(self):
432
+ return [self.typed_prims.index(cp) for cp in self.constrain_prims]
433
+
434
+ @property
435
+ def C(self):
436
+ """Diagonal matrix. Entries for constraints are set to one."""
437
+ size = len(self.typed_prims)
438
+ C = np.zeros((size, size))
439
+ inds = self.constrained_indices
440
+ C[inds, inds] = 1
441
+ return C
442
+
443
+ @property
444
+ def P(self):
445
+ """Projection matrix onto B. See [1] Eq. (4)."""
446
+ if self._P is None:
447
+ P = self.B.dot(self.B_inv)
448
+ # Modify projector, so constrained coordinates are projected out.
449
+ if self.constrain_prims:
450
+ C = self.C
451
+ CPC_inv = svd_inv(C.dot(P).dot(C), thresh=self.svd_inv_thresh)
452
+ P = P - P.dot(C).dot(CPC_inv).dot(C).dot(P)
453
+ self._P = P
454
+ return self._P
455
+
456
+ def transform_forces(self, cart_forces):
457
+ """Combination of Eq. (9) and (11) in [1]."""
458
+ if isinstance(cart_forces, torch.Tensor):
459
+ P = self._as_torch_like(self.P, cart_forces)
460
+ Bt_inv = self._as_torch_like(self.Bt_inv, cart_forces)
461
+ return P @ (Bt_inv @ cart_forces)
462
+ return self.P.dot(self.Bt_inv.dot(cart_forces))
463
+
464
+ def get_K_matrix(self, int_gradient=None):
465
+ if int_gradient is not None:
466
+ assert len(int_gradient) == len(self._primitives)
467
+
468
+ size_ = self.coords3d.size
469
+ if int_gradient is None:
470
+ return np.zeros((size_, size_))
471
+
472
+ K_flat = np.zeros(size_ * size_)
473
+ coords3d = self.coords3d
474
+ for primitive, int_grad_item in zip(self.primitives, int_gradient):
475
+ if hasattr(int_grad_item, "item"):
476
+ int_grad_item = float(int_grad_item.item())
477
+ # Contract with gradient
478
+ try:
479
+ dg = int_grad_item * primitive.jacobian(coords3d)
480
+ # 2nd derivative of normal, but linear, bends is undefined.
481
+ except (ValueError, ZeroDivisionError):
482
+ self.log(
483
+ "Error in calculation of 2nd derivative of primitive "
484
+ f"internal {primitive.indices}."
485
+ )
486
+ continue
487
+ # Depending on the type of internal coordinate dg is a flat array
488
+ # of size 36 (stretch), 81 (bend) or 144 (torsion).
489
+ #
490
+ # An internal coordinate contributes to an element K[j, k] of the
491
+ # K matrix if the cartesian coordinate indices j and k belong to an
492
+ # atom that contributes to the respective internal coordinate.
493
+ #
494
+ # As for now we build up the K matrix as flat array. To add the dg
495
+ # entries at the appropriate places in K_flat we have to calculate
496
+ # the corresponding flat indices of dg in K_flat.
497
+ cart_inds = list(
498
+ it.chain(*[range(3 * i, 3 * i + 3) for i in primitive.indices])
499
+ )
500
+ flat_inds = [
501
+ row * size_ + col for row, col in it.product(cart_inds, cart_inds)
502
+ ]
503
+ K_flat[flat_inds] += dg
504
+ K = K_flat.reshape(size_, size_)
505
+ return K
506
+
507
+ def _as_torch_like(self, array, like):
508
+ if isinstance(array, torch.Tensor):
509
+ return array.to(dtype=like.dtype, device=like.device)
510
+ return torch.tensor(array, dtype=like.dtype, device=like.device)
511
+
512
+ def log_int_grad_msg(self, int_gradient):
513
+ if int_gradient is None:
514
+ self.log(
515
+ "Supplied 'int_gradient' is None. K matrix will be zero, "
516
+ "so derivatives of the\nWilson-B-matrix are neglected in "
517
+ "Hessian transformation."
518
+ )
519
+
520
+ def transform_hessian(self, cart_hessian, int_gradient=None):
521
+ """Transform Cartesian Hessian to internal coordinates."""
522
+ self.log_int_grad_msg(int_gradient)
523
+ K = self.get_K_matrix(int_gradient)
524
+ if isinstance(cart_hessian, torch.Tensor):
525
+ K = self._as_torch_like(K, cart_hessian)
526
+ Bt_inv_prim = self._as_torch_like(self.Bt_inv_prim, cart_hessian)
527
+ B_inv_prim = self._as_torch_like(self.B_inv_prim, cart_hessian)
528
+ return Bt_inv_prim @ (cart_hessian - K) @ B_inv_prim
529
+ return self.Bt_inv_prim.dot(cart_hessian - K).dot(self.B_inv_prim)
530
+
531
+ def backtransform_hessian(self, redund_hessian, int_gradient=None):
532
+ """Transform Hessian in internal coordinates to Cartesians."""
533
+ self.log_int_grad_msg(int_gradient)
534
+ K = self.get_K_matrix(int_gradient)
535
+ if isinstance(redund_hessian, torch.Tensor):
536
+ K = self._as_torch_like(K, redund_hessian)
537
+ B = self._as_torch_like(self.B, redund_hessian)
538
+ return B.T @ redund_hessian @ B + K
539
+ return self.B.T.dot(redund_hessian).dot(self.B) + K
540
+
541
+ def project_hessian(self, H, shift=1000):
542
+ """Expects a hessian in internal coordinates. See Eq. (11) in [1]."""
543
+ P = self.P
544
+ if isinstance(H, torch.Tensor):
545
+ P = self._as_torch_like(P, H)
546
+ eye = torch.eye(P.shape[0], device=H.device, dtype=H.dtype)
547
+ return P @ H @ P + shift * (eye - P)
548
+ return P.dot(H).dot(P) + shift * (np.eye(P.shape[0]) - P)
549
+
550
+ def project_vector(self, vector):
551
+ """Project supplied vector onto range of B."""
552
+ return self.P.dot(vector)
553
+
554
+ def set_primitive_indices(
555
+ self,
556
+ atoms,
557
+ coords3d,
558
+ ):
559
+ coord_info = setup_redundant(
560
+ atoms,
561
+ coords3d,
562
+ factor=self.bond_factor,
563
+ define_prims=self.define_prims,
564
+ min_deg=self.bend_min_deg,
565
+ dihed_max_deg=self.dihed_max_deg,
566
+ lb_min_deg=self.lb_min_deg,
567
+ min_weight=self.min_weight if self.weighted else None,
568
+ tric=self.tric,
569
+ hybrid=self.hybrid,
570
+ hbond_angles=self.hbond_angles,
571
+ freeze_atoms=self.freeze_atoms if self.freeze_atoms_exclude else None,
572
+ internals_with_frozen=self.internals_with_frozen,
573
+ define_for=self.define_for,
574
+ rm_for_frag=self.rm_for_frag,
575
+ logger=self.logger,
576
+ )
577
+
578
+ self.typed_prims = coord_info.typed_prims
579
+ for cp in self.constrain_prims:
580
+ if cp not in self.typed_prims:
581
+ self.typed_prims.append(cp)
582
+
583
+ self.fragments = coord_info.fragments
584
+
585
+ def eval(self, coords3d, attr=None):
586
+ prim_internals = eval_primitives(coords3d, self.primitives)
587
+
588
+ if attr is not None:
589
+ return np.array(
590
+ [getattr(prim_internal, attr) for prim_internal in prim_internals]
591
+ )
592
+
593
+ return prim_internals
594
+
595
+ def transform_int_step(self, int_step, update_constraints=False, pure=False):
596
+ self.log(f"Backtransformation {self.backtransform_counter}")
597
+
598
+ def Bt_inv_prim_getter(cart_coords):
599
+ coords3d = cart_coords.reshape(-1, 3)
600
+ B_prim = np.zeros((len(self.primitives), coords3d.size))
601
+ for i, primitive in enumerate(self.primitives):
602
+ _, gradient = primitive.calculate(coords3d, gradient=True)
603
+ B_prim[i] = gradient
604
+ return self.inv_Bt(B_prim)
605
+
606
+ new_prim_internals, cart_step, failed = transform_int_step(
607
+ int_step,
608
+ self.coords3d.flatten(),
609
+ self.prim_coords,
610
+ self.Bt_inv_prim,
611
+ self.primitives,
612
+ typed_prims=self.typed_prims,
613
+ check_dihedrals=self.rebuild,
614
+ check_bends=self.rebuild,
615
+ bend_min_deg=self.bend_min_deg,
616
+ bend_max_deg=self.lb_min_deg,
617
+ freeze_atoms=self.freeze_atoms,
618
+ constrained_inds=self.constrained_indices,
619
+ update_constraints=update_constraints,
620
+ logger=self.logger,
621
+ Bt_inv_prim_getter=Bt_inv_prim_getter if self.recalc_B else None,
622
+ )
623
+ # Update coordinates
624
+ if not pure:
625
+ self.coords3d += cart_step.reshape(-1, 3)
626
+ self.prim_internals = new_prim_internals
627
+ self.backtransform_counter += 1
628
+ return cart_step
629
+
630
+ def print_typed_prims(self):
631
+ for i, tp in enumerate(self.typed_prims):
632
+ print(i, tp)
633
+
634
+ def __str__(self):
635
+ bonds = len(self.bond_indices)
636
+ bends = len(self.bending_indices)
637
+ dihedrals = len(self.dihedral_indices)
638
+ name = self.__class__.__name__
639
+ return f"{name}({bonds} bonds, {bends} bends, {dihedrals} dihedrals)"
640
+
641
+
642
+ class TRIC(RedundantCoords):
643
+ def __init__(self, *args, **kwargs):
644
+ kwargs["tric"] = True
645
+ kwargs["recalc_B"] = True
646
+ super().__init__(*args, **kwargs)
647
+
648
+
649
+ class TMTRIC(TRIC):
650
+ def __init__(self, atoms, *args, **kwargs):
651
+ tm_indices = get_tm_indices(atoms)
652
+ kwargs.setdefault("rm_for_frag", set()).update(tm_indices)
653
+ super().__init__(atoms, *args, **kwargs)
654
+
655
+
656
+ class HybridRedundantCoords(RedundantCoords):
657
+ def __init__(self, *args, **kwargs):
658
+ kwargs["hybrid"] = True
659
+ super().__init__(*args, **kwargs)