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
pysisyphus/trj.py ADDED
@@ -0,0 +1,824 @@
1
+ import argparse
2
+ import copy
3
+ import itertools as it
4
+ from pathlib import Path
5
+ import re
6
+ import sys
7
+
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
+ import rmsd as rmsd
11
+
12
+ from pysisyphus.constants import BOHR2ANG, AU2KJPERMOL
13
+ from pysisyphus.cos import *
14
+ from pysisyphus.Geometry import Geometry
15
+ from pysisyphus.intcoords.setup import get_fragments
16
+ from pysisyphus.intcoords.PrimTypes import prim_for_human
17
+ from pysisyphus.drivers.merge import hardsphere_merge as hardsphere_merge_driver
18
+ from pysisyphus.helpers import geom_loader, procrustes, get_coords_diffs, shake_coords
19
+ from pysisyphus.helpers_pure import highlight_text
20
+ from pysisyphus.interpolate import *
21
+ from pysisyphus.intcoords.helpers import form_coordinate_union
22
+ from pysisyphus.intcoords.PrimTypes import normalize_prim_input, PrimMap
23
+ from pysisyphus.io.pdb import geom_to_pdb_str
24
+ from pysisyphus.stocastic.align import match_geom_atoms
25
+
26
+
27
+ INTERPOLATE = {
28
+ "idpp": IDPP.IDPP,
29
+ "lst": LST.LST,
30
+ "linear": Interpolator.Interpolator,
31
+ "redund": Redund.Redund,
32
+ "geodesic": Geodesic.Geodesic,
33
+ }
34
+
35
+
36
+ def parse_args(args):
37
+ parser = argparse.ArgumentParser("Utility to transform .xyz and _trj.xyz files.")
38
+
39
+ parser.add_argument(
40
+ "fns",
41
+ nargs="+",
42
+ help="Filenames of .xyz and/or _trj.xyz files (xyz and trj can be mixed).",
43
+ )
44
+
45
+ action_group = parser.add_mutually_exclusive_group(required=True)
46
+ action_group.add_argument(
47
+ "--between", type=int, default=0, help="Interpolate additional images."
48
+ )
49
+ action_group.add_argument(
50
+ "--align", action="store_true", help="Align geometries onto the first geometry."
51
+ )
52
+ action_group.add_argument(
53
+ "--split",
54
+ action="store_true",
55
+ help="Split a supplied geometries in multiple .xyz files.",
56
+ )
57
+ action_group.add_argument(
58
+ "--reverse", action="store_true", help="Reverse a _trj.xyz file."
59
+ )
60
+ action_group.add_argument(
61
+ "--cleantrj",
62
+ action="store_true",
63
+ help="Keep only the first four columns of xyz/trj files.",
64
+ )
65
+ action_group.add_argument(
66
+ "--spline",
67
+ action="store_true",
68
+ help="Evenly redistribute geometries along a splined path.",
69
+ )
70
+ action_group.add_argument(
71
+ "--first", type=int, help="Copy the first N geometries to a new _trj.xyz file."
72
+ )
73
+ action_group.add_argument(
74
+ "--every",
75
+ type=int,
76
+ help="Create new _trj.xyz with every N-th geometry. "
77
+ "Always includes the first and last point.",
78
+ )
79
+ action_group.add_argument(
80
+ "--center",
81
+ action="store_true",
82
+ help="Move the molecules centroid into the origin.",
83
+ )
84
+ action_group.add_argument(
85
+ "--centerm",
86
+ action="store_true",
87
+ help="Move the molecules center of mass into the origin.",
88
+ )
89
+ action_group.add_argument(
90
+ "--translate",
91
+ nargs=3,
92
+ type=float,
93
+ help="Translate the molecule by the given vector given " "in Ångström.",
94
+ )
95
+ action_group.add_argument(
96
+ "--append",
97
+ action="store_true",
98
+ help="Combine the given .xyz files into one .xyz file.",
99
+ )
100
+ action_group.add_argument(
101
+ "--hsmerge",
102
+ action="store_true",
103
+ help="Merge two input geometries into one, while avoiding overlapping atoms "
104
+ "via hardsphere-optimization.",
105
+ )
106
+ action_group.add_argument(
107
+ "--join",
108
+ action="store_true",
109
+ help="Combine the given .xyz/_trj.xyz files into one _trj.xyz file.",
110
+ )
111
+ action_group.add_argument(
112
+ "--match",
113
+ action="store_true",
114
+ help="Resort the second .xyz file so the atom order matches the "
115
+ "first .xyz file. Uses the hungarian method.",
116
+ )
117
+ action_group.add_argument(
118
+ "--std",
119
+ action="store_true",
120
+ help="Move supplied geometry to its standard orientation.",
121
+ )
122
+ action_group.add_argument(
123
+ "--shake", action="store_true", help="Shake (randomly displace) coordiantes."
124
+ )
125
+ action_group.add_argument(
126
+ "--internals",
127
+ action="store_true",
128
+ help="Print automatically generated internal coordinates.",
129
+ )
130
+ action_group.add_argument(
131
+ "--internal-val",
132
+ nargs="+",
133
+ help="Print value(s) of given internal coordinate.",
134
+ )
135
+ action_group.add_argument(
136
+ "--get", type=int, help="Get n-th geometry. Expects 0-based index input."
137
+ )
138
+ action_group.add_argument(
139
+ "--geti", action="store_true", help="Decide on geometry interactively."
140
+ )
141
+ action_group.add_argument(
142
+ "--origin",
143
+ action="store_true",
144
+ help="Translate geometry, so that min(X/Y/Z) == 0.",
145
+ )
146
+ action_group.add_argument(
147
+ "--fragsort", action="store_true", help="Resort atoms by fragments."
148
+ )
149
+ action_group.add_argument(
150
+ "--topdb",
151
+ action="store_true",
152
+ help="Convert given geometry to PDB with automatic fragment detection.",
153
+ )
154
+
155
+ shake_group = parser.add_argument_group()
156
+ shake_group.add_argument(
157
+ "--scale", type=float, default=0.1, help="Scales the displacement in --shake."
158
+ )
159
+ shake_group.add_argument(
160
+ "--seed",
161
+ type=int,
162
+ default=None,
163
+ help="Initialize the RNG for reproducible results.",
164
+ )
165
+
166
+ interpolate_group = parser.add_mutually_exclusive_group()
167
+ interpolate_group.add_argument(
168
+ "--idpp",
169
+ action="store_true",
170
+ help="Interpolate using Image Dependent Pair Potential.",
171
+ )
172
+ interpolate_group.add_argument(
173
+ "--lst", action="store_true", help="Interpolate by linear synchronous transit."
174
+ )
175
+ interpolate_group.add_argument(
176
+ "--redund", action="store_true", help="Interpolate in internal coordinates."
177
+ )
178
+ interpolate_group.add_argument(
179
+ "--geodesic",
180
+ action="store_true",
181
+ help="Geodesic interpolation. Requires the geodesic-interpolate package.",
182
+ )
183
+ parser.add_argument(
184
+ "--extrapolate",
185
+ type=int,
186
+ default=0,
187
+ help="Number of geometries to extrapolate before and after initial and "
188
+ "final geometries.",
189
+ )
190
+ parser.add_argument(
191
+ "--extrapolate-before",
192
+ type=int,
193
+ default=0,
194
+ help="Number of geometries to extrapolate before the initial geometry.",
195
+ )
196
+ parser.add_argument(
197
+ "--extrapolate-after",
198
+ type=int,
199
+ default=0,
200
+ help="Number of geometries to extrapolate after the final geometry.",
201
+ )
202
+ parser.add_argument(
203
+ "--extrapolate-damp",
204
+ type=float,
205
+ default=1.0,
206
+ help="Factor to increase (> 1.0) or damp (< 1.0) extrapolation step size.",
207
+ )
208
+ parser.add_argument(
209
+ "--noipalign",
210
+ action="store_false",
211
+ help="Don't align geometries when interpolating.",
212
+ )
213
+ parser.add_argument(
214
+ "--noxyz", action="store_false", help="Disable dumping of single .xyz files."
215
+ )
216
+ parser.add_argument(
217
+ "--atoms",
218
+ nargs="+",
219
+ type=int,
220
+ default=list(),
221
+ help="Used with --internals. Only print primitives including the given atoms.",
222
+ )
223
+ parser.add_argument(
224
+ "--add_prims",
225
+ type=str,
226
+ default="",
227
+ help="Used with --internals. Define additional primitives. Expects a "
228
+ "string representation of a nested list that can be parsed as YAML "
229
+ "e.g. [[10,30],[1,2,3],[4,5,6,7]].",
230
+ )
231
+
232
+ return parser.parse_args()
233
+
234
+
235
+ def read_geoms(
236
+ xyz_fns,
237
+ coord_type="cart",
238
+ geom_kwargs=None,
239
+ ):
240
+ if geom_kwargs is None:
241
+ geom_kwargs = {}
242
+
243
+ # Single filename
244
+ if isinstance(xyz_fns, str):
245
+ xyz_fns = [xyz_fns]
246
+ names = [""]
247
+ # Dictionary with names as keys and fns as values.
248
+ elif isinstance(xyz_fns, dict):
249
+ names, xyz_fns = zip(*xyz_fns.items())
250
+ # Create empty names for all geometries
251
+ else:
252
+ names = [""] * len(xyz_fns)
253
+
254
+ geoms = list()
255
+ for fn in xyz_fns:
256
+ # Valid for non-inline coordinates
257
+ if Path(fn).suffix or fn.startswith("pubchem:"):
258
+ geoms.extend(
259
+ geom_loader(fn, coord_type=coord_type, iterable=True, **geom_kwargs)
260
+ )
261
+
262
+ # Set names
263
+ for name, geom in zip(names, geoms):
264
+ geom.name = name
265
+ return geoms
266
+
267
+
268
+ def get_geoms(
269
+ xyz_fns,
270
+ coord_type="cart",
271
+ geom_kwargs=None,
272
+ union=False,
273
+ same_prims=True,
274
+ quiet=False,
275
+ # Interpolation related
276
+ interpol_kwargs=None,
277
+ ):
278
+ """Returns a list of Geometry objects in the given coordinate system
279
+ and interpolates if necessary."""
280
+
281
+ if geom_kwargs is None:
282
+ geom_kwargs = {
283
+ "coord_kwargs": {},
284
+ }
285
+
286
+ if union:
287
+ assert coord_type != "cart", "union must not be used with coord_type == cart!"
288
+ union_geoms = read_geoms(union, coord_type=coord_type)
289
+ assert (
290
+ len(union_geoms) == 2
291
+ ), f"Got {len(union_geoms)} geometries for 'union'! Please give only two!"
292
+ geom_kwargs["coord_kwargs"]["typed_prims"] = form_coordinate_union(*union_geoms)
293
+
294
+ geoms = read_geoms(
295
+ xyz_fns,
296
+ coord_type=coord_type,
297
+ geom_kwargs=geom_kwargs,
298
+ )
299
+ if not quiet:
300
+ print(f"Read {len(geoms)} geometr" + ("y" if len(geoms) == 1 else "ies") + ".")
301
+
302
+ atoms_0 = geoms[0].atoms
303
+ # atoms_strs = [" ".join(geom.atoms).lower() for geom in geoms]
304
+ # atoms_0_str = atoms_strs[0]
305
+ # assert all(
306
+ # [atoms_str == atoms_0_str for atoms_str in atoms_strs]
307
+ # ), "Atom ordering/numbering in the geometries is inconsistent!"
308
+
309
+ # TODO:
310
+ # Multistep interpolation (when more than two geometries are specified)
311
+ # in internal coordinates may lead to a different number of defined coordinates.
312
+ # Maybe the union between geom0 and geom1 contains 6 internals and the union
313
+ # betweeen geom1 and geom2 contains 8 primtives. Then the number of coordinates
314
+ # at all images in the final list may be non-constant.
315
+ try:
316
+ interpol_type = interpol_kwargs.pop("type")
317
+ interpol_cls = INTERPOLATE[interpol_type]
318
+ interpolator = interpol_cls(geoms, **interpol_kwargs)
319
+ geoms = interpolator.interpolate_all()
320
+ except AttributeError:
321
+ pass
322
+ except KeyError:
323
+ if interpol_type is not None:
324
+ print(
325
+ f"Unsupported type: '{interpol_type}' given. Valid arguments are "
326
+ f"{list(INTERPOLATE.keys())}'"
327
+ )
328
+
329
+ # Recreate Geometries so they have the correct coord_type. There may
330
+ # be a difference between the coord_type used for interpolation and
331
+ # the desired coord_type as specified in the function arguments.
332
+ if coord_type != geoms[0].coord_type:
333
+ recreated_geoms = list()
334
+ for geom in geoms:
335
+ geom_kwargs_ = copy.deepcopy(geom_kwargs)
336
+ try:
337
+ typed_prims = {
338
+ "typed_prims": geom.internal.typed_prims,
339
+ }
340
+ geom_kwargs_["coord_kwargs"]["typed_prims"] = typed_prims
341
+ except AttributeError:
342
+ typed_prims = None
343
+
344
+ if coord_type == "cart":
345
+ geom_kwargs_["coord_kwargs"] = {}
346
+
347
+ geom = Geometry(
348
+ geom.atoms,
349
+ geom.cart_coords,
350
+ coord_type=coord_type,
351
+ **geom_kwargs_,
352
+ )
353
+ recreated_geoms.append(geom)
354
+ geoms = recreated_geoms
355
+
356
+ if not same_prims or (coord_type == "cart"):
357
+ return geoms
358
+
359
+ geom_prim_inds = [geom.internal.typed_prims for geom in geoms]
360
+ first_set = geom_prim_inds[0]
361
+ same_prim_inds = all([ith_set == first_set for ith_set in geom_prim_inds[1:]])
362
+ # Recreate geometries with the same primitive internal coordinates
363
+ if not same_prim_inds:
364
+ typed_prims = form_coordinate_union(geoms[0], geoms[-1])
365
+ geom_kwargs["coord_kwargs"]["typed_prims"] = typed_prims
366
+ geoms = [
367
+ Geometry(atoms_0, geom.cart_coords, coord_type=coord_type, **geom_kwargs)
368
+ for geom in geoms
369
+ ]
370
+ assert all(
371
+ [len(geom.internal.typed_prims) == len(typed_prims) for geom in geoms]
372
+ )
373
+ return geoms
374
+
375
+
376
+ def standardize_geoms(geoms, coord_type, geom_kwargs, same_prims=True, union=False):
377
+ atoms0 = geoms[0].atoms
378
+ same_atoms = all([geom.atoms == atoms0 for geom in geoms[1:]])
379
+
380
+ if union and coord_type != "cart":
381
+ union_geoms = read_geoms(union, coord_type=coord_type)
382
+ assert (
383
+ len(union_geoms) == 2
384
+ ), f"Got {len(union_geoms)} geometries for 'union'! Please supply only two!"
385
+ geom_kwargs["coord_kwargs"]["typed_prims"] = form_coordinate_union(*union_geoms)
386
+ elif (
387
+ same_atoms
388
+ and same_prims
389
+ and (len(geoms)) > 1
390
+ and (coord_type not in ("cart", "cartesian"))
391
+ ):
392
+ # Use 'tric', if requested; otherwise always use 'redund'
393
+ sp_coord_type = "tric" if coord_type == "tric" else "redund"
394
+ geom_0 = geoms[0].copy(coord_type=sp_coord_type)
395
+ geom_m1 = geoms[-1].copy(coord_type=sp_coord_type)
396
+ typed_prims = form_coordinate_union(geom_0, geom_m1)
397
+ geom_kwargs["coord_kwargs"]["typed_prims"] = typed_prims
398
+
399
+ return [
400
+ Geometry(geom.atoms, geom.cart_coords, coord_type=coord_type, **geom_kwargs)
401
+ for geom in geoms
402
+ ]
403
+
404
+
405
+ def dump_geoms(
406
+ geoms,
407
+ fn_base,
408
+ trj_infix="",
409
+ dump_trj=True,
410
+ dump_xyz=True,
411
+ dump_pdb=False,
412
+ ang=False,
413
+ ):
414
+ xyz_per_geom = [geom.as_xyz() for geom in geoms]
415
+ if dump_trj:
416
+ trj_str = "\n".join(xyz_per_geom)
417
+ trj_fn = f"{fn_base}{trj_infix}_trj.xyz"
418
+ with open(trj_fn, "w") as handle:
419
+ handle.write(trj_str)
420
+ print(f"Wrote all geometries to {trj_fn}.")
421
+ if dump_xyz:
422
+ for i, xyz in enumerate(xyz_per_geom):
423
+ geom_fn = f"{fn_base}.geom_{i:03d}.xyz"
424
+ with open(geom_fn, "w") as handle:
425
+ handle.write(xyz)
426
+ print(f"Wrote geom {i:03d} to {geom_fn}.")
427
+ elif dump_pdb:
428
+ for i, geom in enumerate(geoms):
429
+ geom_fn = f"{fn_base}.geom_{i:03d}.pdb"
430
+ pdb_str = geom_to_pdb_str(geom, detect_fragments=True)
431
+ with open(geom_fn, "w") as handle:
432
+ handle.write(pdb_str)
433
+ print(f"Wrote geom {i:03d} to {geom_fn}.")
434
+ print()
435
+
436
+
437
+ def align(geoms):
438
+ """Align all geometries onto the first using partical procrustes."""
439
+ cos = ChainOfStates.ChainOfStates(geoms)
440
+ procrustes(cos)
441
+ return [geom for geom in cos.images]
442
+
443
+
444
+ def spline_redistribute(geoms):
445
+ szts = SimpleZTS.SimpleZTS(geoms)
446
+ pre_diffs = get_coords_diffs([image.coords for image in szts.images])
447
+ szts.reparametrize()
448
+ post_diffs = get_coords_diffs([image.coords for image in szts.images])
449
+ cds_str = lambda cds: " ".join([f"{cd:.2f}" for cd in cds])
450
+ print("Normalized path segments before splining:")
451
+ print(cds_str(pre_diffs))
452
+ print("Normalized path segments after redistribution along spline:")
453
+ print(cds_str(post_diffs))
454
+ return szts.images
455
+
456
+
457
+ def every(geoms, every_nth):
458
+ # every_nth_geom = geoms[::every_nth]
459
+ # The first geometry is always present, but the last geometry
460
+ # may be missing.
461
+ sampled_indices = list(range(0, len(geoms), every_nth))
462
+ if sampled_indices[-1] != len(geoms) - 1:
463
+ sampled_indices.append(len(geoms) - 1)
464
+ sampled_inds_str = ", ".join([str(i) for i in sampled_indices])
465
+ print(f"Sampled indices {sampled_inds_str}")
466
+ # if every_nth_geom[-1] != geoms[-1]:
467
+ # every_nth_geom.append(geoms[-1])
468
+ every_nth_geom = [geoms[i] for i in sampled_indices]
469
+ return every_nth_geom
470
+
471
+
472
+ def center(geoms):
473
+ for geom in geoms:
474
+ geom.coords3d = geom.coords3d - geom.centroid
475
+ return geoms
476
+
477
+
478
+ def centerm(geoms):
479
+ for geom in geoms:
480
+ geom.coords3d = geom.coords3d - geom.center_of_mass
481
+ return geoms
482
+
483
+
484
+ def translate(geoms, trans):
485
+ for geom in geoms:
486
+ geom.coords3d += trans
487
+ return geoms
488
+
489
+
490
+ def append(geoms):
491
+ atoms = geoms[0].atoms * len(geoms)
492
+ coords = list(it.chain([geom.coords for geom in geoms]))
493
+ return [
494
+ Geometry(atoms, coords),
495
+ ]
496
+
497
+
498
+ def hardsphere_merge(geoms):
499
+ assert len(geoms) == 2
500
+ union = hardsphere_merge_driver(*geoms)
501
+ return [
502
+ union,
503
+ ]
504
+
505
+
506
+ def match(ref_geom, geom_to_match):
507
+ rmsd_before = rmsd.kabsch_rmsd(ref_geom.coords3d, geom_to_match.coords3d)
508
+ print(f"Kabsch RMSD before: {rmsd_before:.4f}")
509
+ matched_geom = match_geom_atoms(ref_geom, geom_to_match, hydrogen=True)
510
+
511
+ # Right now the atoms are not in the right order as we only sorted the
512
+ # individual coord blocks by atom.
513
+ # This dictionary will hold the counter indices for the individual atom
514
+ atom_type_inds = {atom: 0 for atom in ref_geom.atom_types}
515
+ matched_coord_blocks, _ = matched_geom.coords_by_type
516
+ new_coords = list()
517
+ for atom in ref_geom.atoms:
518
+ # Get the current counter/index from the dicitonary for the given atom
519
+ cur_atom_ind = atom_type_inds[atom]
520
+ # Select the appropriate atom from the coords block
521
+ atom_coords = matched_coord_blocks[atom][cur_atom_ind]
522
+ new_coords.append(atom_coords)
523
+ # Increment the counter so the next time the same atom type comes up
524
+ # we fetch the next entry of the coord block.
525
+ atom_type_inds[atom] += 1
526
+ # Assign the updated atom order and corresponding coordinates
527
+ matched_geom.atoms = ref_geom.atoms
528
+ matched_geom.coords = np.array(new_coords).flatten()
529
+ rmsd_after = rmsd.kabsch_rmsd(ref_geom.coords3d, matched_geom.coords3d)
530
+ print(f"Kabsch RMSD after: {rmsd_after:.4f}")
531
+ return [
532
+ matched_geom,
533
+ ]
534
+
535
+
536
+ def standard_orientation(geoms):
537
+ [geom.standard_orientation() for geom in geoms]
538
+ return geoms
539
+
540
+
541
+ def shake(geoms, scale=0.1, seed=None):
542
+ for geom in geoms:
543
+ geom.coords = shake_coords(geom.coords, scale=scale, seed=seed)
544
+ return geoms
545
+
546
+
547
+ def print_internals(geoms, filter_atoms=None, add_prims=""):
548
+ if filter_atoms is None:
549
+ filter_atoms = list()
550
+
551
+ for i, geom in enumerate(geoms):
552
+ print(highlight_text(f"{i:03d}: {geom}"))
553
+ atoms = geom.atoms
554
+ atom_num = len(atoms)
555
+ atom_inds = set(range(atom_num))
556
+ filter_set = set(filter_atoms)
557
+ # Atom indices must superset of filter_atoms
558
+ invalid = filter_set - atom_inds
559
+ assert not invalid, (
560
+ f"Filter indices {invalid} are outside of the "
561
+ f"valid range for the {i}-th geometry '{geom}' with {atom_num} "
562
+ f"atoms (valid indices: range(0,{atom_num}))."
563
+ )
564
+
565
+ int_geom = Geometry(
566
+ atoms,
567
+ geom.coords,
568
+ coord_type="redund",
569
+ )
570
+ int_ = int_geom.internal
571
+
572
+ prim_counter = 0
573
+ prev_len = None
574
+
575
+ zipped = zip(int_.typed_prims, int_._prim_internals)
576
+ for j, ((pt, *inds), pi) in enumerate(zipped):
577
+ if filter_set and not (set(inds) & filter_set):
578
+ continue
579
+
580
+ val = pi.val
581
+ len_ = len(pi.inds)
582
+
583
+ if prev_len is None:
584
+ prev_len = len(inds)
585
+ val, unit = prim_for_human(pt, val)
586
+
587
+ if len_ > prev_len:
588
+ prim_counter = 0
589
+ print()
590
+
591
+ inds_str = ", ".join([f"{atoms[i]: >2s}{i: <4d}" for i in inds])
592
+ inds_str = f"[{inds_str}]"
593
+ print(
594
+ f"{j:04d}: {pt: >20} {prim_counter:03d} {inds_str} {val: >10.4f}"
595
+ f"{unit}"
596
+ )
597
+ prim_counter += 1
598
+ prev_len = len_
599
+
600
+ print(f"Printed {j+1} primitive internals.")
601
+ print()
602
+
603
+
604
+ def print_internal_vals(geoms, typed_prim):
605
+ ((prim_type, *indices),) = normalize_prim_input(typed_prim)
606
+ prim = PrimMap[prim_type](indices)
607
+
608
+ indices_str = ", ".join(map(str, indices))
609
+ print(f"Values for coordinate ({prim_type}, {indices_str})")
610
+ for i, geom in enumerate(geoms):
611
+ val = prim.calculate(geom.coords3d)
612
+ val_conv, unit = prim_for_human(prim_type, val)
613
+ print(f"{i:03d}: {val:.6f} (au), ({val_conv:.4f} {unit})")
614
+
615
+
616
+ def get(geoms, index):
617
+ # Convert to positive index. Right now this doesn't do anything useful.
618
+ # Could be used to generated more meaningful filenames if we could somehow
619
+ # also return a index number to generate the filename.
620
+ if index < 0:
621
+ index += len(geoms)
622
+ return [
623
+ geoms[index],
624
+ ]
625
+
626
+
627
+ class GotNoGeometryException(Exception):
628
+ pass
629
+
630
+
631
+ def get_interactively(geoms):
632
+ # Try to parse energies from geoms
633
+ energy_re = re.compile(r"[-\.\d]+")
634
+ energies = list()
635
+ for geom in geoms:
636
+ mobj = energy_re.search(geom.comment)
637
+ if mobj:
638
+ energy = float(mobj[0])
639
+ else:
640
+ energy = np.nan
641
+ energies.append(energy)
642
+ energies = np.array(energies)
643
+ energies -= np.nanmin(energies)
644
+ energies *= AU2KJPERMOL
645
+ min_ind = np.nanargmin(energies)
646
+ print(f"Minimum energy at index {min_ind}")
647
+ print("(q) to quit\n(p) to plot energies")
648
+ msg = f"Input index (0-{len(geoms)-1})/p/q: "
649
+ while True:
650
+ try:
651
+ selection = input(msg)
652
+ if selection == "q":
653
+ raise GotNoGeometryException()
654
+ elif selection == "p":
655
+ fig, ax = plt.subplots()
656
+ ax.plot(energies, "o-")
657
+ ax.set_xlabel("Index")
658
+ ax.set_ylabel("ΔE / kJ mol⁻¹")
659
+ plt.show()
660
+ continue
661
+ selection = int(selection)
662
+ except ValueError:
663
+ print("Invalid input!")
664
+ continue
665
+ en = energies[selection]
666
+ print(f"ΔE at geometry {selection} is {en:+.2f} kJ mol⁻¹.")
667
+ yn = input("Get this geometry (y/n)? ").lower()
668
+ if yn == "y":
669
+ return get(geoms, selection)
670
+ else:
671
+ continue
672
+
673
+
674
+ def origin(geoms):
675
+ for i, geom in enumerate(geoms):
676
+ print(f"{i:02d}: {geom}")
677
+ geom.coords3d -= geom.coords3d.min(axis=0)
678
+ print(f"\tmax(coords3d): {geom.coords3d.max(axis=0)}")
679
+ return geoms
680
+
681
+
682
+ def frag_sort(geoms):
683
+ sorted_geoms = list()
684
+ for i, geom in enumerate(geoms):
685
+ print(f"{i:02d}: {geom}")
686
+ frags = get_fragments(geom.atoms, geom.coords)
687
+ print(f"\tFound {len(frags)} fragments.\n" "\tResorting atoms and coordinates.")
688
+ new_indices = list(it.chain(*frags))
689
+ new_atoms = [geom.atoms[i] for i in new_indices]
690
+ new_coords = geom.coords3d[new_indices]
691
+ sorted_geoms.append(Geometry(new_atoms, new_coords))
692
+ return sorted_geoms
693
+
694
+
695
+ def run():
696
+ args = parse_args(sys.argv[1:])
697
+
698
+ if args.idpp:
699
+ interpol_type = "idpp"
700
+ elif args.lst:
701
+ interpol_type = "lst"
702
+ elif args.redund:
703
+ interpol_type = "redund"
704
+ elif args.geodesic:
705
+ interpol_type = "geodesic"
706
+ elif args.between:
707
+ interpol_type = "linear"
708
+ else:
709
+ interpol_type = None
710
+
711
+ interpol_kwargs = {
712
+ "type": interpol_type,
713
+ "between": args.between,
714
+ "align": args.noipalign,
715
+ "extrapolate": args.extrapolate,
716
+ "extrapolate_before": args.extrapolate_before,
717
+ "extrapolate_after": args.extrapolate_after,
718
+ "extrapolate_damp": args.extrapolate_damp,
719
+ }
720
+
721
+ # Read supplied files and create Geometry objects
722
+ geoms = get_geoms(
723
+ args.fns,
724
+ interpol_kwargs=interpol_kwargs,
725
+ )
726
+
727
+ to_dump = geoms
728
+ dump_trj = True
729
+ dump_xyz = args.noxyz
730
+ dump_pdb = False
731
+ trj_infix = ""
732
+ if args.between:
733
+ fn_base = "interpolated"
734
+ elif args.align:
735
+ to_dump = align(geoms)
736
+ fn_base = "aligned"
737
+ elif args.split:
738
+ fn_base = "split"
739
+ dump_trj = False
740
+ elif args.reverse:
741
+ to_dump = geoms[::-1]
742
+ fn_base = "reversed"
743
+ elif args.cleantrj:
744
+ fn_base = "cleaned"
745
+ elif args.first:
746
+ to_dump = geoms[: args.first]
747
+ fn_base = "first"
748
+ trj_infix = f"_{args.first}"
749
+ elif args.spline:
750
+ to_dump = spline_redistribute(geoms)
751
+ fn_base = "splined"
752
+ elif args.every:
753
+ to_dump = every(geoms, args.every)
754
+ fn_base = "every"
755
+ trj_infix = f"_{args.every}th"
756
+ elif args.center:
757
+ to_dump = center(geoms)
758
+ fn_base = "centered"
759
+ elif args.centerm:
760
+ to_dump = centerm(geoms)
761
+ fn_base = "centeredm"
762
+ elif args.translate:
763
+ trans = np.array(args.translate) / BOHR2ANG
764
+ to_dump = translate(geoms, trans)
765
+ fn_base = "translated"
766
+ elif args.append:
767
+ to_dump = append(geoms)
768
+ fn_base = "appended"
769
+ elif args.hsmerge:
770
+ to_dump = hardsphere_merge(geoms)
771
+ fn_base = "hs_merged"
772
+ elif args.join:
773
+ to_dump = geoms
774
+ fn_base = "joined"
775
+ elif args.match:
776
+ to_dump = match(*geoms)
777
+ fn_base = "matched"
778
+ elif args.std:
779
+ to_dump = standard_orientation(geoms)
780
+ fn_base = "standard"
781
+ elif args.shake:
782
+ to_dump = shake(geoms, args.scale, args.seed)
783
+ fn_base = "shaked"
784
+ elif args.get or (args.get == 0):
785
+ to_dump = get(geoms, args.get)
786
+ fn_base = "got"
787
+ elif args.geti:
788
+ try:
789
+ to_dump = get_interactively(geoms)
790
+ fn_base = "got"
791
+ except GotNoGeometryException:
792
+ return
793
+ elif args.internals:
794
+ print_internals(geoms, args.atoms, args.add_prims)
795
+ return
796
+ elif args.internal_val:
797
+ print_internal_vals(geoms, args.internal_val)
798
+ return
799
+ elif args.fragsort:
800
+ to_dump = frag_sort(geoms)
801
+ fn_base = "frag_sorted"
802
+ elif args.origin:
803
+ origin(geoms)
804
+ fn_base = "origin"
805
+ elif args.topdb:
806
+ fn_base = "as_pdb"
807
+ dump_pdb = True
808
+ dump_xyz = False
809
+
810
+ # Write transformed geometries
811
+ dump_trj = dump_trj and (len(to_dump) > 1)
812
+
813
+ dump_geoms(
814
+ to_dump,
815
+ fn_base,
816
+ trj_infix=trj_infix,
817
+ dump_trj=dump_trj,
818
+ dump_xyz=dump_xyz,
819
+ dump_pdb=dump_pdb,
820
+ )
821
+
822
+
823
+ if __name__ == "__main__":
824
+ run()