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,74 @@
1
+ import struct
2
+
3
+ import numpy as np
4
+
5
+
6
+ NINE_ZEROS = struct.pack("d" * 9, *[0.0] * 9)
7
+ EYE3 = struct.pack("d" * 9, *np.eye(3).flatten())
8
+
9
+
10
+ def send_closure(sock, hdrlen, fmts, verbose=False):
11
+ def send_msg(msg, fmt=None, packed=False):
12
+ """Send message through socket.
13
+
14
+ If fmt is None and the message is not packed (converted to bytes),
15
+ it is either convert to bytes via struct with the given fmt, or it
16
+ is assumed to be a str and encoded as ascii. Strings are padded
17
+ to a length of hdrlen (header length, default=12).
18
+
19
+ If it is already packed the message is sent as is, e.g. for the
20
+ virial, which is already in bytes (packed).
21
+ """
22
+ if not packed:
23
+ if fmt == "floats":
24
+ msg = struct.pack(fmts[fmt], *msg)
25
+ elif fmt is not None:
26
+ msg = struct.pack(fmts[fmt], msg)
27
+ else:
28
+ msg = f"{msg: <{hdrlen}}".encode("ascii")
29
+ if verbose:
30
+ print(f"SENDING: {msg}")
31
+ sock.sendall(msg)
32
+
33
+ return send_msg
34
+
35
+
36
+ """Vllt. recv_msg so verändern, dass es, wenn ein fmt gegeben ist auch
37
+ automatisch die korrekte anzahl an bytes erwartet"""
38
+
39
+
40
+ def recv_closure(sock, hdrlen, fmts, verbose=False):
41
+ def recv_msg(nbytes=None, fmt=None, expect=""):
42
+ """Receive a message of given length from the socket.
43
+
44
+ If nbytes is not given we expect hdrlen (header length) bytes.
45
+ If fmt is given the message is also unpacked using struct, otherwise
46
+ ascii bytes are assumed and a string is returned.
47
+ """
48
+ if nbytes is None:
49
+ nbytes = hdrlen
50
+
51
+ msg = sock.recv(nbytes)
52
+ if fmt:
53
+ msg = struct.unpack(fmts[fmt], msg)
54
+ else:
55
+ msg = msg.decode("ascii").strip()
56
+ if verbose:
57
+ if expect:
58
+ expect = f", expected '{expect}'"
59
+
60
+ print(f"RECEIVED: {msg}{expect}")
61
+ return msg
62
+
63
+ return recv_msg
64
+
65
+
66
+ def get_fmts(cartesians):
67
+ fmts = {
68
+ "int": "i", # Atom number
69
+ "float": "d", # Energy
70
+ "nine_floats": "d" * 9, # (Inverse) cell vectors, virial, zeros
71
+ "floats": "d" * cartesians, # Forces
72
+ "floats_sq": "d" * cartesians**2, # Hessian
73
+ }
74
+ return fmts
@@ -0,0 +1,132 @@
1
+ # See [1] 10.1002/jcc.21026
2
+
3
+ import itertools as it
4
+
5
+ import numpy as np
6
+ import rmsd
7
+
8
+ from pysisyphus.Geometry import Geometry
9
+ from pysisyphus.linalg import get_rot_mat
10
+ from pysisyphus.stocastic.Kick import Kick
11
+
12
+
13
+ class FragmentKick(Kick):
14
+ def __init__(
15
+ self,
16
+ geom,
17
+ fragments,
18
+ fix_fragments=None,
19
+ displace_from=None,
20
+ random_displacement=False,
21
+ **kwargs,
22
+ ):
23
+ super().__init__(geom, **kwargs)
24
+
25
+ self.fragments = self.get_fragments(fragments)
26
+ if fix_fragments is None:
27
+ fix_fragments = (0,)
28
+ self.fix_fragments = fix_fragments
29
+
30
+ if displace_from is None:
31
+ displace_from = list()
32
+ self.displace_from = displace_from
33
+ # True when displace_from is given
34
+ self.random_displacement = self.displace_from or random_displacement
35
+
36
+ frag_coords = [self.initial_geom.coords3d[frag] for frag in self.fragments]
37
+ # Shift centroids of the fragment into the cartesian origin
38
+ self.frag_coords = [fc - fc.mean(axis=0) for fc in frag_coords]
39
+ atoms_arr = np.array(self.initial_geom.atoms)
40
+ self.frag_atoms = [atoms_arr[frag] for frag in self.fragments]
41
+
42
+ new_coords3d = np.concatenate(self.frag_coords)
43
+ geom = Geometry(list(it.chain(*self.frag_atoms)), new_coords3d.flatten())
44
+ with open("fragments_in_origin.xyz", "w") as handle:
45
+ handle.write(geom.as_xyz())
46
+
47
+ self.frag_inds_flat = list(it.chain(*self.fragments))
48
+ # Given an array with fragment coordinates these indices will sort the
49
+ # array into the original atom order.
50
+ self.sort_inds = np.argsort(self.frag_inds_flat)
51
+ self.print_fragments()
52
+
53
+ def print_fragments(self):
54
+ lines = self.initial_geom.as_xyz().split("\n")[2:]
55
+ for i, fragment in enumerate(self.fragments):
56
+ self.log(f"Fragment {i}:")
57
+ frag_lines = [lines[j] for j in fragment]
58
+ self.log("\n".join(frag_lines) + "\n")
59
+
60
+ def get_fragments(self, fragments):
61
+ fragments = list(fragments)
62
+ # Compare number of atoms defined in fragments with the total
63
+ # number of atoms in the molecule.
64
+ fragment_atoms = list(it.chain(*fragments))
65
+ if len(fragment_atoms) != len(self.atoms):
66
+ self.log(
67
+ "Fragments were not fully specified. " "Determining remaining fragment."
68
+ )
69
+ all_indices = set(range(len(self.atoms)))
70
+ new_fragment = list(all_indices - set(fragment_atoms))
71
+ fragments.append(new_fragment)
72
+
73
+ fragments = [np.array(frag) for frag in fragments]
74
+ return fragments
75
+
76
+ def get_origin(self):
77
+ if not self.random_displacement:
78
+ return (0, 0, 0)
79
+
80
+ # Coordinates of fixed fragment
81
+ frag_coords = self.frag_coords[self.fix_fragments[0]]
82
+ """Here we have two options:
83
+ 1.) displace_from is not given, so we use all possible
84
+ atom indices in the fragment.
85
+ 2.) displace_from is given, so we restrict the valid
86
+ atom indices. This way some degree of symmetry can
87
+ be considered and the generated structures should
88
+ be more similar. This should reduce the effort in
89
+ matching the geometries later on with the hungarian
90
+ method.
91
+
92
+ Consider a stocastic search for minima between benzene and
93
+ a chlorine molecule. As all carbons/hydrogens in the benzene
94
+ are equivalent one could greatly reduce the effort of matching
95
+ atoms later on, when displacing only from one carbon and one
96
+ hydrogen."""
97
+ if not self.displace_from:
98
+ frag_indices = np.arange(frag_coords.shape[0])
99
+ else:
100
+ frag_indices = self.displace_from
101
+
102
+ # Select random atom of fixed fragment
103
+ random_ind = np.random.choice(frag_indices, size=1)[0]
104
+ return frag_coords[random_ind]
105
+
106
+ def kick_fragment(self, frag_coords):
107
+ R = get_rot_mat()
108
+ kick = self.get_kick()[:3]
109
+ # Fragment rotation
110
+ rot_coords = R.dot(frag_coords.T).T
111
+ # Fragment translation
112
+ rot_kicked_coords = rot_coords + self.get_origin() + kick
113
+ return rot_kicked_coords
114
+
115
+ def get_input_geom(self, geom):
116
+ kicked_frags = list()
117
+ for i, fc in enumerate(self.frag_coords):
118
+ if i in self.fix_fragments:
119
+ kicked_frag = fc
120
+ else:
121
+ kicked_frag = self.kick_fragment(fc)
122
+ kicked_frags.append(kicked_frag)
123
+ new_coords3d = np.concatenate(kicked_frags)
124
+ # As the fragments are not necessarily defined in order, especially
125
+ # when we generate the second fragment automatically, we have to
126
+ # sort the coordinates, so they are in the original order. E.g. the
127
+ # fragments ((4, 3), (2, 1, 0)) would result 'new_coords3d' with the
128
+ # coordinates of atom 4 at index 0, coordinates of atom 3 at index 1, etc.
129
+ new_coords3d = new_coords3d[self.sort_inds]
130
+ new_coords = rmsd.kabsch_rotate(new_coords3d, self.initial_coords3d).flatten()
131
+ new_geom = Geometry(self.atoms, new_coords)
132
+ return new_geom
@@ -0,0 +1,81 @@
1
+ import numpy as np
2
+ import rmsd
3
+
4
+ from pysisyphus.stocastic.Pipeline import Pipeline
5
+
6
+
7
+ class Kick(Pipeline):
8
+
9
+ def __init__(self, geom, radius=0.5, **kwargs):
10
+ super().__init__(geom, **kwargs)
11
+
12
+ self.radius = radius
13
+
14
+ def get_kick(self):
15
+ # Interval [0, 1)
16
+ kick = np.random.random(4*self.coords_size)
17
+ # Stretch [0, 1) to [-r ... r)
18
+ kick = self.radius * (2*kick - 1)
19
+ kick = kick.reshape(-1, 3)
20
+ # Filter for kicks within a sphere with radius self.radius
21
+ kick_lengths = np.linalg.norm(kick, axis=1)
22
+ valid_kicks = kick[kick_lengths <= self.radius].flatten()
23
+ # Do a recursion when not enough valid kicks were found
24
+ if valid_kicks.size < self.coords_size:
25
+ valid_kicks = self.get_kick()
26
+ # Don't return more than we need
27
+ return valid_kicks[:self.coords_size]
28
+
29
+ def get_input_geom(self, geom):
30
+ kick = self.get_kick()
31
+ new_geom = geom.copy()
32
+ new_coords = new_geom.coords + kick
33
+ # Rotate the newly generated coordinates on the initial
34
+ # coordinates.
35
+ # TODO: this may be not needed as we align+match later on ...
36
+ new_coords = rmsd.kabsch_rotate(new_coords.reshape(-1, 3),
37
+ self.initial_coords3d
38
+ ).flatten()
39
+ new_geom.coords = new_coords
40
+ return new_geom
41
+
42
+ def run_cycle(self, geom):
43
+ print(f"##### Cycle {self.cur_cycle:03d}, "
44
+ f"Micro Cycle {self.cur_micro_cycle:03d} #####")
45
+ opt_geoms = [self.run_kicked_geom(geom) for i in range(self.cycle_size)]
46
+ # Filter out None
47
+ opt_geoms = [geom for geom in opt_geoms if geom]
48
+ opt_num = len(opt_geoms)
49
+ print(f"{opt_num}/{self.cycle_size} optimizations converged.")
50
+
51
+ # Comparing to the initial geometry is only useful when the initial
52
+ # geometry is optimized. Otherwise all (small) kicks will converge
53
+ # to the same optimized structure, that is still very different from
54
+ # the initial one.
55
+ """
56
+ initial_rmsds = [
57
+ rmsd.kabsch_rmsd(self.initial_coords3d, ogeom.coords3d)
58
+ for ogeom in opt_geoms
59
+ ]
60
+ print("Initial RMDS")
61
+ print(initial_rmsds)
62
+ """
63
+ kept_geoms = self.get_unique_geometries(opt_geoms)
64
+
65
+ # cycle_str = f"{self.cur_cycle:03d}_{self.cur_micro_cycle:03d}"
66
+ # fn_base = f"cycle_{cycle_str}"
67
+ # trj_fn = f"{fn_base}_trj.xyz"
68
+ # with open(trj_fn, "w") as handle:
69
+ # handle.write(make_trj_str_from_geoms(opt_geoms))
70
+
71
+ # trj_filtered_fn = f"{fn_base}_filtered_trj.xyz"
72
+ # with open(trj_filtered_fn, "w") as handle:
73
+ # handle.write(make_trj_str_from_geoms(kept_geoms))
74
+
75
+ # for i, ogeom in enumerate(kept_geoms):
76
+ # fn = f"geom_{i:02d}_{cycle_str}.xyz"
77
+ # with open(fn, "w") as handle:
78
+ # handle.write(ogeom.as_xyz())
79
+ self.cur_micro_cycle += 1
80
+
81
+ return kept_geoms
@@ -0,0 +1,303 @@
1
+ import bisect
2
+ import itertools as it
3
+ import logging
4
+ import time
5
+
6
+ import numpy as np
7
+ import rmsd
8
+ from scipy.spatial.distance import pdist
9
+
10
+ from pysisyphus.calculators.XTB import XTB
11
+ from pysisyphus.helpers import check_for_end_sign
12
+ from pysisyphus.helpers_pure import highlight_text
13
+ from pysisyphus.intcoords.setup import get_pair_covalent_radii
14
+ from pysisyphus.optimizers.RFOptimizer import RFOptimizer
15
+ from pysisyphus.stocastic.align import matched_rmsd
16
+ from pysisyphus.xyzloader import make_trj_str_from_geoms
17
+
18
+
19
+ class Pipeline:
20
+
21
+ def __init__(self, geom, calc_getter=None, seed=None, max_cycles=5, cycle_size=15,
22
+ rmsd_thresh=.1, energy_thresh=1e-3, energy_range=.125,
23
+ compare_num=25, break_after=2,
24
+ calc_kwargs=None):
25
+ self.initial_geom = geom
26
+ self.calc_getter = calc_getter
27
+ self.max_cycles = max_cycles
28
+ self.cycle_size = cycle_size
29
+ if seed is None:
30
+ self.seed = int(time.time())
31
+ else:
32
+ self.seed = seed
33
+ self.rmsd_thresh = rmsd_thresh
34
+ self.energy_thresh = energy_thresh
35
+ self.energy_range = energy_range
36
+ self.compare_num = compare_num
37
+ self.break_after = break_after
38
+ self.calc_kwargs = {
39
+ "charge": 0,
40
+ "mult": 1,
41
+ }
42
+ if calc_kwargs is not None:
43
+ self.calc_kwargs.update(calc_kwargs)
44
+
45
+ np.random.seed(self.seed)
46
+ self.logger = logging.getLogger("stocastic")
47
+
48
+ self.is_analytical2d = len(geom.atoms) == 1
49
+ self.log(f"Seed: {self.seed}")
50
+ self.coords_size = self.initial_geom.coords.size
51
+
52
+ self.calc_counter = 0
53
+ self.cur_cycle = 0
54
+ self.cur_micro_cycle = 0
55
+ # Indicates if the next cycle is the last one
56
+ self.break_in = self.break_after
57
+
58
+ self.new_geoms = []
59
+ self.new_energies = []
60
+
61
+ self.initial_coords3d = self.initial_geom.coords3d
62
+ self.atoms = self.initial_geom.atoms
63
+
64
+ def __str__(self):
65
+ return f"{self.__class__.__name__}(seed={self.seed})"
66
+
67
+ def log(self, message):
68
+ """Write a log message.
69
+
70
+ Wraps the logger variable.
71
+
72
+ Parameters
73
+ ----------
74
+ message : str
75
+ Message to be logged.
76
+ """
77
+
78
+ self.logger.debug(f"{message}")
79
+
80
+ def get_valid_index_set(self, to_intersect):
81
+ return set(range(len(self.new_energies))) & set(to_intersect)
82
+
83
+ def atoms_are_too_close(self, geom, factor=.7):
84
+ """Determine if atoms are too close."""
85
+ dist_mat = pdist(geom.coords3d)
86
+ cov_rad_mat = get_pair_covalent_radii(geom.atoms)
87
+ too_close = dist_mat < factor*cov_rad_mat
88
+ return any(too_close)
89
+
90
+ def geom_is_close_in_energy(self, geom):
91
+ energy = geom.energy
92
+ i = bisect.bisect_left(self.new_energies, energy)
93
+ # Determine if there are neighbours that are close in energy
94
+ # as we insert left/before the most similary energy the indices
95
+ # of the (to be) neighbours in the current self.new_energies list
96
+ # are i-1 and i.
97
+ valid_inds = self.get_valid_index_set((i-1, i))
98
+ diffs = [abs(self.new_energies[j] - energy) for j in valid_inds]
99
+ return len(diffs) > 0 and min(diffs) < self.energy_thresh
100
+
101
+ def geom_is_new(self, geom):
102
+ """Determine if geometry is not already known."""
103
+ if len(self.new_geoms) == 0:
104
+ self.log("Found first geometry!")
105
+ return True
106
+
107
+ i = bisect.bisect_left(self.new_energies, geom.energy)
108
+ to_intersect = range(i-self.compare_num, i+self.compare_num)
109
+ valid_inds = np.array(list(self.get_valid_index_set(to_intersect)))
110
+ new_energies = np.array(self.new_energies)[valid_inds]
111
+ # Restrict geometries for RMSD comparison to an energy range
112
+ # around the energy of the geometry to check.
113
+ in_range = np.abs(new_energies - geom.energy) < self.energy_range
114
+ valid_inds = valid_inds[in_range]
115
+ # If this evalutes to True the energy of the current in geometry is
116
+ # quite different and we add the geometry.
117
+ if valid_inds.size == 0:
118
+ # print("Energy of geometry is very different from the remaining "
119
+ # "ones. Adding geometry!")
120
+ reason = "different energy."
121
+ is_new = True
122
+ # Otherwise check the RMSD values for the remaining geometries that
123
+ # are close in energy.
124
+ else:
125
+ rmsds = [matched_rmsd(geom, self.new_geoms[i])[0] for i in valid_inds]
126
+ rmsds = np.array(rmsds)
127
+ is_new = rmsds.min() > self.rmsd_thresh
128
+ reason = f"different RMSD (min(RMSD) = {rmsds.min():.3f})"
129
+
130
+ if is_new:
131
+ self.log(f"Found new geometry based on {reason}")
132
+ return is_new
133
+
134
+ def geom_is_valid(self, geom):
135
+ """Filter out geometries that are None, or were the atoms are too close
136
+ or when they are already known."""
137
+
138
+ valid = (geom is not None
139
+ and not self.geom_is_close_in_energy(geom)
140
+ )
141
+
142
+ if not self.is_analytical2d:
143
+ valid = (valid
144
+ and not self.atoms_are_too_close(geom)
145
+ and self.geom_is_new(geom)
146
+ )
147
+ return valid
148
+
149
+ def get_input_geom(self, geom):
150
+ raise Exception("Implement me!")
151
+
152
+ """
153
+ def run_new_geom(self, geom):
154
+ input_geom = self.get_input_geom(geom)
155
+ input_coords = input_geom.coords.copy()
156
+
157
+ # Check if the geometry is similar to an already known starting
158
+ # geometry.
159
+ overlaps = new_coords.dot(np.array(self.starting_coords).T)/self.coords_size
160
+ # print("overlaps with already known starting coordinates")
161
+ # print(overlaps)
162
+ max_overlap_ind = overlaps.argmax()
163
+ max_overlap = overlaps[max_overlap_ind]
164
+ similar_fn = f"similar_{self.similar_ind:03d}_trj.xyz"
165
+ # print(f"max overlap is {max_overlap:.1f}, {similar_fn}, index {max_overlap_ind}")
166
+ max_coords = self.starting_coords[max_overlap_ind]
167
+ self.similar_ind += 1
168
+ rmsds = list()
169
+ for sc in self.starting_coords:
170
+ sc3d = sc.reshape(-1, 3)
171
+ rm = rmsd.kabsch_rmsd(new_coords.reshape(-1,3), sc3d)
172
+ rmsds.append(rm)
173
+ rmsds = np.array(rmsds)
174
+ self.starting_coords.append(new_coords)
175
+ """
176
+
177
+ def get_unique_geometries(self, geoms):
178
+ geom_num = len(geoms)
179
+ rmsds = np.full((geom_num, geom_num), np.inf)
180
+ for i, j in it.combinations(range(geom_num), 2):
181
+ coords1 = geoms[i].coords.reshape(-1, 3)
182
+ coords2 = geoms[j].coords.reshape(-1, 3)
183
+ rmsds[i, j] = rmsd.kabsch_rmsd(coords1, coords2)
184
+ is_, js = np.where(rmsds < self.rmsd_thresh)
185
+ similar_inds = np.unique(js)
186
+ unique_geoms = [geoms[i] for i in range(geom_num) if i not in similar_inds]
187
+ return unique_geoms
188
+
189
+ def run_geom_opt(self, geom):
190
+ if self.calc_getter is not None:
191
+ calc = self.calc_getter(calc_number=self.calc_counter)
192
+ geom.set_calculator(calc)
193
+ opt_kwargs = {
194
+ "gdiis": False,
195
+ "thresh": "gau_loose",
196
+ "overachieve_factor": 2,
197
+ "max_cycles": 75,
198
+ }
199
+ opt = RFOptimizer(geom, **opt_kwargs)
200
+ opt.run()
201
+ opt_geom = geom if opt.is_converged else None
202
+ else:
203
+ calc = XTB(calc_number=self.calc_counter, **self.calc_kwargs)
204
+ opt_result = calc.run_opt(geom.atoms, geom.coords, keep=False)
205
+ try:
206
+ opt_geom = opt_result.opt_geom
207
+ except AttributeError:
208
+ opt_geom = None
209
+
210
+ self.calc_counter += 1
211
+ return opt_geom
212
+
213
+ def write_geoms_to_trj(self, geoms, fn, comments=None):
214
+ with open(fn, "w") as handle:
215
+ handle.write(
216
+ make_trj_str_from_geoms(geoms, comments, energy_comments=True)
217
+ )
218
+
219
+ def run(self):
220
+ for self.cur_cycle in range(self.max_cycles):
221
+ cycle_start = time.time()
222
+ self.log(highlight_text(f"Cycle {self.cur_cycle}"))
223
+ input_geoms = [self.get_input_geom(self.initial_geom)
224
+ for _ in range(self.cycle_size)]
225
+ # Write input geometries to disk
226
+ self.write_geoms_to_trj(input_geoms, f"cycle_{self.cur_cycle:03d}_input_trj.xyz")
227
+ # Run optimizations on input geometries
228
+ calc_start = time.time()
229
+ opt_geoms = list()
230
+ for i, geom in enumerate(input_geoms, 1):
231
+ print(f"Optimizing geometry {i:03d}/{self.cycle_size:03d}", end="\r")
232
+ opt_geoms.append(self.run_geom_opt(geom))
233
+ print()
234
+ calc_end = time.time()
235
+ calc_duration = calc_end - calc_start
236
+ self.log(f"Optimizations took {calc_duration:.0f} s.")
237
+
238
+ kept_geoms = list()
239
+ for geom in opt_geoms:
240
+ # Do all the filtering and reject all invalid geometries
241
+ if not self.geom_is_valid(geom):
242
+ continue
243
+
244
+ energy = geom.energy
245
+ i = bisect.bisect_left(self.new_energies, energy)
246
+ self.new_energies.insert(i, energy)
247
+ self.new_geoms.insert(i, geom)
248
+ kept_geoms.append(geom)
249
+ if i == 0 and len(self.new_energies) > 1:
250
+ last_minimum = self.new_energies[1]
251
+ diff = abs(energy - last_minimum)
252
+ self.log(f"It is a new global minimum at {energy:.4f} au! "
253
+ f"Last one was {diff:.4f} au higher.")
254
+
255
+ kept_num = len(kept_geoms)
256
+
257
+ trj_filtered_fn = f"cycle_{self.cur_cycle:03d}_trj.xyz"
258
+ # Sort by energy
259
+ kept_geoms = sorted(kept_geoms, key=lambda g: g.energy)
260
+ if kept_geoms:
261
+ self.write_geoms_to_trj(kept_geoms, trj_filtered_fn)
262
+ self.log(f"Kicks in cycle {self.cur_cycle} produced "
263
+ f"{kept_num} new geometries.")
264
+ self.break_in = self.break_after
265
+ elif self.break_in == 0:
266
+ self.log("Didn't find any new geometries in the last "
267
+ f"{self.break_after} cycles. Exiting!")
268
+ break
269
+ else:
270
+ self.log(f"Cycle {self.cur_cycle} produced no new geometries.")
271
+ self.break_in -= 1
272
+
273
+ cycle_end = time.time()
274
+ cycle_duration = cycle_end - cycle_start
275
+ self.log(f"Cycle {i} took {cycle_duration:.0f} s.")
276
+ self.log("")
277
+ if check_for_end_sign():
278
+ break
279
+
280
+ self.log(f"Run produced {len(self.new_energies)} geometries!")
281
+ # Return empty list of nothing was found
282
+ if not self.new_energies:
283
+ return []
284
+
285
+ fn = "final_trj.xyz"
286
+ self.write_geoms_to_trj(self.new_geoms, fn)
287
+ # self.new_energies = np.array(new_energies)
288
+ np.savetxt("energies.dat", self.new_energies)
289
+ first_geom = self.new_geoms[0]
290
+ first_geom.standard_orientation()
291
+ first_geom.energy = self.new_energies[0]
292
+
293
+ if self.is_analytical2d:
294
+ return self.new_geoms
295
+
296
+ matched_geoms = [first_geom, ]
297
+ for geom, energy in zip(self.new_geoms[1:], self.new_energies):
298
+ rmsd, (_, matched_geom) = matched_rmsd(first_geom, geom)
299
+ matched_geom.energy = energy
300
+ matched_geoms.append(matched_geom)
301
+ fn_matched = "final_matched_trj.xyz"
302
+ self.write_geoms_to_trj(matched_geoms, fn_matched)
303
+ return matched_geoms
@@ -0,0 +1,21 @@
1
+ import logging
2
+ import sys
3
+
4
+ from pysisyphus.stocastic.Kick import Kick
5
+ from pysisyphus.stocastic.FragmentKick import FragmentKick
6
+
7
+
8
+ __all__ = [
9
+ "FragmentKick",
10
+ "Kick",
11
+ ]
12
+
13
+ logger = logging.getLogger("stocastic")
14
+ logger.setLevel(logging.DEBUG)
15
+ fh = logging.FileHandler("stocastic.log", mode="w", delay=True)
16
+ fh.setLevel(logging.DEBUG)
17
+ logger.addHandler(logging.StreamHandler(sys.stdout))
18
+ # fmt_str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
19
+ # formatter = logging.Formatter(fmt_str)
20
+ # fh.setFormatter(formatter)
21
+ logger.addHandler(fh)