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,649 @@
1
+ import collections.abc
2
+ from difflib import SequenceMatcher
3
+ from enum import Enum
4
+ import itertools as it
5
+ import json
6
+ import logging
7
+ import math
8
+ from pathlib import Path
9
+ from typing import List, Tuple
10
+ import re
11
+ import uuid
12
+ import time
13
+ from typing import Optional
14
+
15
+ import numpy as np
16
+ from numpy.typing import NDArray
17
+ import psutil
18
+
19
+ from pysisyphus.config import p_DEFAULT, T_DEFAULT
20
+ from pysisyphus.constants import AU2J, BOHR2ANG, C, R, AU2KJPERMOL, NA
21
+
22
+ import torch
23
+
24
+ """Functions defined here don't import anything from pysisyphus, besides
25
+ the constants module, but only from the stdlib and from third parties."""
26
+
27
+
28
+ HASH_PREC = 4
29
+
30
+
31
+ def eigval_to_wavenumber(ev):
32
+ # This approach seems numerically more unstable
33
+ # conv = AU2J / (AMU2KG * BOHR2M ** 2) / (2 * np.pi * 3e10)**2
34
+ # w2nu = np.sign(ev) * np.sqrt(np.abs(ev) * conv)
35
+ # The two lines below are adopted from Psi4 and seem more stable,
36
+ # compared to the approach above.
37
+ conv = np.sqrt(NA * AU2J * 1.0e19) / (2 * np.pi * C * BOHR2ANG)
38
+ w2nu = np.sign(ev) * np.sqrt(np.abs(ev)) * conv
39
+ return w2nu
40
+
41
+
42
+ def hash_arr(arr, precision=HASH_PREC):
43
+ str_ = np.array2string(arr, precision=precision)
44
+ return hash(str_)
45
+
46
+
47
+ def hash_args(*args, precision=HASH_PREC):
48
+ hashes = list()
49
+ for arg in args:
50
+ try:
51
+ hash_ = hash(arg)
52
+ except TypeError:
53
+ hash_ = hash_arr(arg, precision=precision)
54
+ hashes.append(hash_)
55
+ return hash(tuple(hashes))
56
+
57
+
58
+ def log(logger, msg, level=logging.DEBUG):
59
+ if logger is not None:
60
+ logger.log(level, msg)
61
+
62
+
63
+ def sort_by_central(set1, set2):
64
+ """Determines a common index in two sets and returns a length 3
65
+ tuple with the central index at the middle position and the two
66
+ terminal indices as first and last indices."""
67
+ central_set = set1 & set2
68
+ union = set1 | set2
69
+ assert len(central_set) == 1
70
+ terminal1, terminal2 = union - central_set
71
+ (central,) = central_set
72
+ return (terminal1, central, terminal2), central
73
+
74
+
75
+ def merge_sets(fragments):
76
+ """Merge a list of iterables."""
77
+ # Hold the final fragments that can't be merged further, as they
78
+ # contain distinct atoms.
79
+ fragments = [frozenset(frag) for frag in fragments]
80
+ merged = list()
81
+ while len(fragments) > 0:
82
+ popped = fragments.pop(0)
83
+ # Look for an intersection between the popped unmerged fragment
84
+ # and the remaining unmerged fragments.
85
+ for frag in fragments:
86
+ if popped & frag:
87
+ fragments.remove(frag)
88
+ # If a intersecting unmerged fragment is found merge
89
+ # both fragments and append them at the end.
90
+ fragments.append(popped | frag)
91
+ break
92
+ else:
93
+ # Add the unmerged fragment into merged if it doesn't
94
+ # intersect with any other unmerged fragment.
95
+ merged.append(popped)
96
+ return merged
97
+
98
+
99
+ def remove_duplicates(seq):
100
+ tuples = [tuple(itm) for itm in seq]
101
+ seen = set()
102
+ seen_add = seen.add
103
+ return [itm for itm in tuples if not (itm in seen or seen_add(itm))]
104
+
105
+
106
+ class OrderedEnum(Enum):
107
+ def __ge__(self, other):
108
+ if self.__class__ is other.__class__:
109
+ return self.value >= other.value
110
+ return NotImplemented
111
+
112
+ def __gt__(self, other):
113
+ if self.__class__ is other.__class__:
114
+ return self.value > other.value
115
+ return NotImplemented
116
+
117
+ def __le__(self, other):
118
+ if self.__class__ is other.__class__:
119
+ return self.value <= other.value
120
+ return NotImplemented
121
+
122
+ def __lt__(self, other):
123
+ if self.__class__ is other.__class__:
124
+ return self.value < other.value
125
+ return NotImplemented
126
+
127
+ def __str__(self):
128
+ return self.name
129
+
130
+
131
+ def timed(logger=None):
132
+ def decorator(func):
133
+ def wrapper(*args, **kwargs):
134
+ start = time.time()
135
+ result = func(*args, **kwargs)
136
+ end = time.time()
137
+ duration = end - start
138
+ msg = f"Execution of '{func.__name__}' took {duration:.2f} s."
139
+ if logger is not None:
140
+ log(logger, msg)
141
+ else:
142
+ print(msg)
143
+ return result
144
+
145
+ return wrapper
146
+
147
+ return decorator
148
+
149
+
150
+ def get_input(data, prompt, lbl_func=None):
151
+ if lbl_func is None:
152
+ lbl_func = lambda _: _
153
+ labels = [lbl_func(d) for d in data]
154
+ print(prompt)
155
+ while True:
156
+ for i, l in enumerate(labels):
157
+ print(f"{i: >3d}: {l}")
158
+ try:
159
+ inp = int(input("Selection: "))
160
+ if not (0 <= inp < len(labels)):
161
+ raise ValueError
162
+ break
163
+ except ValueError:
164
+ print("Invalid input!")
165
+ print()
166
+ return data[inp]
167
+
168
+
169
+ def expand(to_expand):
170
+ if any([isinstance(to_expand, cls) for cls in (list, tuple, np.ndarray)]):
171
+ return to_expand
172
+ elif ".." in to_expand:
173
+ start, end = [int(i) for i in to_expand.split("..")]
174
+ return list(range(start, end))
175
+ # Numbers
176
+ else:
177
+ return [int(to_expand)]
178
+
179
+
180
+ def full_expand(to_expand):
181
+ try:
182
+ split = to_expand.strip().split(",")
183
+ expanded = list(it.chain(*[expand(te) for te in split]))
184
+ except AttributeError:
185
+ try:
186
+ expanded = list(to_expand)
187
+ except TypeError:
188
+ expanded = [
189
+ to_expand,
190
+ ]
191
+ return expanded
192
+
193
+
194
+ def file_or_str(*args, method=False, mode="r", exact=False):
195
+ exts = args
196
+
197
+ def inner_func(func):
198
+ def wrapped(inp, *args, **kwargs):
199
+ if method:
200
+ obj = inp
201
+ inp, *args = args
202
+ p = Path(inp)
203
+ looks_like_file = exts and (
204
+ (p.suffix in exts) or (exact and p.name in exts)
205
+ )
206
+ if looks_like_file and p.is_file():
207
+ with open(p, mode=mode) as handle:
208
+ inp = handle.read()
209
+ elif looks_like_file and not p.exists():
210
+ raise FileNotFoundError(
211
+ f"{inp} looks like a file/path, but it does not exist!"
212
+ )
213
+ if method:
214
+ res = func(obj, inp, *args, **kwargs)
215
+ else:
216
+ res = func(inp, *args, **kwargs)
217
+ return res
218
+
219
+ return wrapped
220
+
221
+ return inner_func
222
+
223
+
224
+ def recursive_update(d, u):
225
+ """Recursive update of d with keys/values from u.
226
+
227
+ From https://stackoverflow.com/questions/3232943
228
+ """
229
+ if u is None:
230
+ return d
231
+
232
+ # Best I can do is ... secretly transform hierarchical input
233
+ # to old-style flat input.
234
+ #
235
+ # Try to transform new-style hierarchical input
236
+ # to the old-style flat input. If this new-style proves useful
237
+ # and everything is refactored accordingly the try/except could
238
+ # be dropped. But now everything is still in the old-style.
239
+ try:
240
+ keys = list(u["type"].keys())
241
+ if len(keys) == 1:
242
+ key = keys[0]
243
+ kwargs = u["type"][key]
244
+ u["type"] = key
245
+ try:
246
+ u.update(kwargs)
247
+ # Raised when kwargs is None, e.g., the input is hierarchical,
248
+ # but no further keywords are provided for the given type.
249
+ except TypeError:
250
+ pass
251
+ # Raised when u has no "type" key
252
+ except KeyError:
253
+ pass
254
+ except AttributeError:
255
+ pass
256
+
257
+ for k, v in u.items():
258
+ if isinstance(v, collections.abc.Mapping):
259
+ d[k] = recursive_update(d.get(k, {}), v)
260
+ else:
261
+ d[k] = v
262
+ return d
263
+
264
+
265
+ def report_isotopes(geom, affect_str):
266
+ if (geom.isotopes is not None) and len(geom.isotopes) > 0:
267
+ print(f"Different isotopes were requested! This will affect {affect_str}.")
268
+ atoms = geom.atoms
269
+ masses = geom.masses
270
+ for atom_ind, _ in geom.isotopes:
271
+ print(f"\tAtom {atom_ind}{atoms[atom_ind]}: {masses[atom_ind]:.6f} au")
272
+ print()
273
+
274
+
275
+ def report_frozen_atoms(geom):
276
+ if hasattr(geom, "freeze_atoms") and len(geom.freeze_atoms) > 0:
277
+ print(f"Coordinates of {len(geom.freeze_atoms)} atoms are frozen:")
278
+ atoms = geom.atoms
279
+ coords3d = geom.coords3d * BOHR2ANG
280
+ fmt = " >12.8f"
281
+ entries = list()
282
+ for atom_ind in geom.freeze_atoms:
283
+ atom = atoms[atom_ind]
284
+ x, y, z = coords3d[atom_ind]
285
+ entries.append(f"{atom_ind:03d} {atom} {x:{fmt}} {y:{fmt}} {z:{fmt}}")
286
+ print("\t" + " ".join(entries))
287
+ print()
288
+
289
+
290
+ def highlight_text(text, width=80, level=0):
291
+ levels = {
292
+ # horizontal
293
+ # vertical
294
+ # corner
295
+ 0: ("#", "#", "#"),
296
+ 1: ("-", "|", "+"),
297
+ }
298
+ full_length = len(text) + 4
299
+ pad_len = width - full_length
300
+ pad_len = (pad_len - (pad_len % 2)) // 2
301
+ pad = " " * pad_len
302
+ hchar, vchar, cornerchar = levels[level]
303
+ full_row = cornerchar + (hchar * (full_length - 2)) + cornerchar
304
+ highlight = (
305
+ f"""{pad}{full_row}\n{pad}{vchar} {text.upper()} {vchar}\n{pad}{full_row}"""
306
+ )
307
+ return highlight
308
+
309
+
310
+ def interpolate_colors(values, c1, c2, num=32):
311
+ """Expects two RGB colors c1 and c2."""
312
+ c_diff = c2 - c1
313
+ step = c_diff / (num - 1)
314
+ colors = (c1 + np.arange(num)[:, None] * step).astype(int)
315
+
316
+ # Map value interval onto interval range(num)
317
+ # y = m*x + n
318
+ val_min = values.min()
319
+ val_max = values.max()
320
+ m = abs((num - 1) / (val_min - val_max))
321
+ n = -m * val_min
322
+ inds = np.around(m * values + n).astype(int)
323
+ rgb_colors = colors[inds]
324
+ hex_colors = [f"#{r:02x}{g:02x}{b:02x}" for r, g, b in rgb_colors]
325
+ return rgb_colors, hex_colors
326
+
327
+
328
+ def get_molecular_radius(coords3d, min_offset=0.9452):
329
+ coords3d = coords3d.copy()
330
+ mean = coords3d.mean(axis=0)
331
+ coords3d -= mean[None, :]
332
+ distances = np.linalg.norm(coords3d, axis=1)
333
+ # When this function is utilized in a HardSphere calculator
334
+ # the radii will be at least '2 * (2 * std)' apart,
335
+ # as the std-offset will be added two times to all radii.
336
+ std = max(min_offset, np.std(distances))
337
+ radius = distances.mean() + 2 * std
338
+ return radius
339
+
340
+
341
+ def filter_fixture_store(test_name):
342
+ def inner_function(function):
343
+ def wrapper(fixture_store):
344
+ rb = fixture_store["results_bag"]
345
+ filtered = {
346
+ "results_bag": {k: v for k, v in rb.items() if k.startswith(test_name)}
347
+ }
348
+ return function(filtered)
349
+
350
+ return wrapper
351
+
352
+ return inner_function
353
+
354
+
355
+ def get_clock():
356
+ ref = time.time()
357
+
358
+ def clock(msg=""):
359
+ nonlocal ref
360
+ now = time.time()
361
+ dur = now - ref
362
+ ref = now
363
+ print(f"{msg: >32}, {dur:.3f} s since last call!")
364
+
365
+ return clock
366
+
367
+
368
+ def chunks(l, n):
369
+ """Yield successive n-sized chunks from l.
370
+ https://stackoverflow.com/a/312464
371
+ """
372
+ for i in range(0, len(l), n):
373
+ yield l[i : i + n]
374
+
375
+
376
+ def describe(arr):
377
+ shape = arr.shape
378
+ min_ = arr.min()
379
+ max_ = arr.max()
380
+ mean = np.mean(arr)
381
+ median = np.median(arr)
382
+ var = np.var(arr)
383
+ fmt = ".4f"
384
+ return (
385
+ f"shape={shape}, min={min_:{fmt}}, mean={mean:{fmt}}, "
386
+ f"median={median:{fmt}}, max={max_:{fmt}}, variance={var:.4e}"
387
+ )
388
+
389
+
390
+ def touch(fn):
391
+ try:
392
+ Path(fn).touch()
393
+ except IsADirectoryError:
394
+ pass
395
+
396
+
397
+ def approx_float(
398
+ num: float, expected: float, abs_tol: float = 1e-6, rel_tol: float = 1e-12
399
+ ) -> bool:
400
+ def pos_or_none(num, name):
401
+ assert (num > 0.0) or (num is None), f"{name} must be positive or None!"
402
+
403
+ pos_or_none(abs_tol, "Absolute tolerance")
404
+ pos_or_none(rel_tol, "Relative tolerance")
405
+ assert abs_tol or rel_tol
406
+
407
+ if rel_tol is None:
408
+ rel_tol = 0.0
409
+ rel_tol = rel_tol * expected
410
+ tolerance = max(abs_tol, rel_tol)
411
+ return abs(num - expected) <= tolerance
412
+
413
+
414
+ _CONV_FUNCS = {
415
+ # First item in value converts to JSON dumpable type,
416
+ # second items converts from JSON to original type.
417
+ "energy": (lambda en: float(en), lambda en: float(en)),
418
+ "all_energies": (
419
+ lambda all_energies: all_energies.tolist(),
420
+ lambda all_energies: np.array(all_energies, dtype=float),
421
+ ),
422
+ "forces": (
423
+ lambda forces: forces.tolist(),
424
+ lambda forces: np.array(forces, dtype=float).flatten(),
425
+ ),
426
+ "hessian": (
427
+ lambda hessian: hessian.tolist(),
428
+ lambda hessian: np.array(hessian, dtype=float),
429
+ ),
430
+ }
431
+
432
+
433
+ def results_to_json(results):
434
+ conv_results = {key: _CONV_FUNCS[key][0](val) for key, val in results.items()}
435
+ return json.dumps(conv_results)
436
+
437
+
438
+ def json_to_results(as_json):
439
+ results = {
440
+ key: _CONV_FUNCS[key][1](val) for key, val in json.loads(as_json).items()
441
+ }
442
+ return results
443
+
444
+
445
+ def standard_state_corr(T=T_DEFAULT, p=p_DEFAULT, n=1):
446
+ """dG for change of standard state from gas to solution of 1 mol/l"""
447
+ Vm = n * R * T / p # in m³
448
+ Vm_litres = Vm * 1000
449
+ dG = R * T * math.log(Vm_litres) / 1000 # kJ mol⁻¹
450
+ dG_au = dG / AU2KJPERMOL
451
+ return dG_au
452
+
453
+
454
+ def check_mem(mem, pal, avail_frac=0.85, logger=None):
455
+ virt_mem = psutil.virtual_memory()
456
+ mb_available = virt_mem.available * avail_frac / 1024 / 1024
457
+ mb_requested = mem * pal
458
+ msg = f"{mb_available:.2f} MB memory available, {mb_requested:.2f} MB requested."
459
+ if mb_requested > mb_available:
460
+ mb_corr = int(mb_available / pal)
461
+ msg += f" Too much memory requested. Using smaller value of {mb_corr} MB."
462
+ else:
463
+ mb_corr = mem
464
+ log(logger, msg)
465
+
466
+ return mb_corr
467
+
468
+
469
+ def get_ratio(str_: str, comp_str: str) -> str:
470
+ """See https://stackoverflow.com/a/17388505"""
471
+ return SequenceMatcher(None, str_, comp_str).ratio()
472
+
473
+
474
+ def find_closest_sequence(str_: str, comp_strs: List[str]) -> Tuple[str, float]:
475
+ ratios = [get_ratio(str_, comp_str) for comp_str in comp_strs]
476
+ argmax = np.argmax(ratios)
477
+ max_ratio = ratios[argmax]
478
+ best_match = comp_strs[argmax]
479
+ return (best_match, max_ratio)
480
+
481
+
482
+ def increment_fn(org_fn: str, suffix: Optional[str] = None) -> str:
483
+ """
484
+ Append, or increase a suffixed counter on a given filename.
485
+ If no counter is present it will be set to zero. Otherwise
486
+ it is incremented by one.
487
+
488
+ >>> increment_fn("opt", "rebuilt")
489
+ 'opt_rebuilt_000'
490
+ >>> increment_fn("opt")
491
+ 'opt_000'
492
+ >>> increment_fn("opt_rebuilt_000", "rebuilt")
493
+ 'opt_rebuilt_001'
494
+
495
+ Parameters
496
+ ----------
497
+ org_fn
498
+ The original, unaltered filename.
499
+ suffix
500
+ Optional suffix to be append.
501
+
502
+ Returns
503
+ -------
504
+ incr_fn
505
+ Modified filename with optional suffix and incremented counter.
506
+ """
507
+ if suffix is not None and len(suffix) > 0:
508
+ suffix = f"_{suffix}"
509
+ else:
510
+ suffix = ""
511
+
512
+ # Check if prefix is present
513
+ regex = re.compile(rf"(.+)({suffix}_(\d+))")
514
+ if mobj := regex.match(org_fn):
515
+ org_fn, _, counter = mobj.groups()
516
+ counter = int(counter)
517
+ counter += 1
518
+ else:
519
+ counter = 0
520
+
521
+ incr_fn = f"{org_fn}{suffix}_{counter:03d}"
522
+ return incr_fn
523
+
524
+
525
+ def get_box(coords3d, offset=0.0):
526
+ mins = coords3d.min(axis=0)
527
+ maxs = coords3d.max(axis=0)
528
+ box = np.stack((mins, maxs), axis=1)
529
+ box[:, 0] -= offset
530
+ box[:, 1] += offset
531
+ return box
532
+
533
+
534
+ def molecular_volume(
535
+ coords3d: NDArray[float],
536
+ vdw_radii: NDArray[float],
537
+ n_trial: int = 10_000,
538
+ offset: float = 1.0,
539
+ ) -> Tuple[float, float, float]:
540
+ """Monte-Carlo estimate of molecular volume using Van der Waals spheres.
541
+ Cartesian coordinates and VdW-radii are expected in Bohr!
542
+ """
543
+ box = get_box(coords3d, offset=offset)
544
+ edges = np.diff(box, axis=1).flatten()
545
+ box_volume = abs(np.prod(edges))
546
+ # print(f"Box with volume {box_volume:.2f} a0³ and dimensions\n{box}")
547
+
548
+ def trial_points(n):
549
+ tps = np.random.rand(n, 3)
550
+ box_min = box[:, 0]
551
+ return box_min + tps * edges
552
+
553
+ tps = trial_points(n=n_trial)
554
+
555
+ dists = np.linalg.norm(coords3d[:, None, :] - tps, axis=2)
556
+ below_radius = dists <= vdw_radii[:, None]
557
+ below_radius = below_radius.sum()
558
+ ratio = below_radius / n_trial
559
+ mol_vol = ratio * box_volume # a0³ / Molecule
560
+ mol_vol_ang3 = mol_vol * BOHR2ANG**3 # ų / Molecule
561
+ molar_vol = mol_vol_ang3 * NA * 1e-24 # l/mol
562
+ return mol_vol, mol_vol_ang3, molar_vol
563
+
564
+
565
+ def get_cubic_crystal(l, n=2, atom="Na"):
566
+ coords3d = list()
567
+ atoms = list()
568
+ for xn in range(-n, n + 1):
569
+ for yn in range(-n, n + 1):
570
+ for zn in range(-n, n + 1):
571
+ coords3d.append((l * xn, l * yn, l * zn))
572
+ atoms.append(atom)
573
+ coords3d = np.array(coords3d)
574
+ return atoms, coords3d
575
+
576
+
577
+ # Unicode subscript characters for numbers
578
+ _NUM_SUBSCRIPTS = {str(i): chr(int(f"0x208{i}", 16)) for i in range(10)}
579
+
580
+
581
+ def to_subscript_num(num):
582
+ return "".join([_NUM_SUBSCRIPTS[c] for c in str(num)])
583
+
584
+
585
+ def to_sets(iterable):
586
+ return set([frozenset(i) for i in iterable])
587
+
588
+
589
+ def cache_arrays(sources, dest):
590
+ dest_path = Path(dest).with_suffix(".npz")
591
+
592
+ def cache_decorator(func):
593
+ def wrapped_func(*args, **kwargs):
594
+ if dest_path.exists():
595
+ npzfile = np.load(dest_path)
596
+ arrays = tuple([npzfile[src] for src in sources])
597
+ else:
598
+ arrays = func(*args, **kwargs)
599
+ assert len(arrays) == len(sources)
600
+ kwds = {src: arr for src, arr in zip(sources, arrays)}
601
+ np.savez(dest_path, **kwds)
602
+ return arrays
603
+
604
+ return wrapped_func
605
+
606
+ return cache_decorator
607
+
608
+
609
+ def estimate(gen, elems):
610
+ tot_dur = 0.0
611
+ for i in range(1, elems + 1):
612
+ start = time.time()
613
+ elem = next(gen)
614
+ dur = time.time() - start
615
+ tot_dur += dur
616
+ ran_ratio = i / elems
617
+ elems_left = elems - i
618
+ dur_per_elem = tot_dur / i
619
+ est_dur = dur_per_elem * elems_left
620
+ est_dur_min = est_dur / 60
621
+ print(f"{ran_ratio: >8.2%} finished ... {est_dur_min: >8.2f} min left")
622
+ yield elem
623
+
624
+
625
+ def get_random_path(stem=""):
626
+ if stem != "":
627
+ stem += "_"
628
+ while (path := Path(f"{stem}{uuid.uuid1()}")).exists():
629
+ pass
630
+ return path
631
+
632
+
633
+ def kill_dir(path):
634
+ """Innocent function remove a directory.
635
+
636
+ It must contain only files and no other directories.
637
+ So this won't do too much damage hopefully.
638
+ """
639
+ for fn in Path(path).iterdir():
640
+ fn.unlink()
641
+ path.rmdir()
642
+
643
+
644
+ def rms(arr):
645
+ if isinstance(arr, torch.Tensor):
646
+ result = torch.sqrt(torch.mean(arr**2))
647
+ else:
648
+ result = np.sqrt(np.mean(arr**2))
649
+ return result
@@ -0,0 +1,50 @@
1
+ import logging
2
+ from pathlib import Path
3
+
4
+ from distributed import Client
5
+
6
+ from pysisyphus.helpers import slugify_worker
7
+
8
+ LOGGERS = {
9
+ "calculator": "calculator.log",
10
+ "wfoverlap": "wfoverlap.log",
11
+ }
12
+
13
+
14
+ def get_fh_logger(name, log_fn):
15
+ """Initialize a logger with 'name', level DEBUG and a FileHandler."""
16
+ logger = logging.getLogger(name)
17
+ logger.setLevel(logging.DEBUG)
18
+ if len(logger.handlers) == 0:
19
+ fh = logging.FileHandler(log_fn, mode="w", delay=True)
20
+ fh.setLevel(logging.DEBUG)
21
+ # fmt_str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
22
+ # fmt_str = "%(asctime)s - %(message)s"
23
+ fmt_str = "%(asctime)s - %(message)s"
24
+ datefmt = "%y-%m-%d %H:%M:%S"
25
+ formatter = logging.Formatter(fmt_str, datefmt=datefmt)
26
+ fh.setFormatter(formatter)
27
+ logger.addHandler(fh)
28
+ # Uncommented this for now as the host is already in the filename
29
+ # logger.debug(f"Initialized logging on {platform.node()}")
30
+
31
+
32
+ def init_logging_base(dask_worker, log_path):
33
+ """Prepare individual loggers for one dask_worker."""
34
+ slug = slugify_worker(dask_worker.worker_address)
35
+ for name, log_fn_base in LOGGERS.items():
36
+ log_fn = log_path / f"{slug}_{log_fn_base}"
37
+ get_fh_logger(name, log_fn)
38
+
39
+
40
+ def init_logging(log_dir="./", scheduler=None):
41
+ """Prepare the logger in log_path. When called with scheduler
42
+ loggers for every worker are prepared."""
43
+ log_path = Path(log_dir)
44
+ if scheduler:
45
+ client = Client(scheduler)
46
+ client.run(init_logging_base, log_path=log_path)
47
+ else:
48
+ for name, log_fn_base in LOGGERS.items():
49
+ log_fn = log_path / log_fn_base
50
+ get_fh_logger(name, log_fn)