DescripyTor 0.3.2__tar.gz → 0.4.3__tar.gz

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.

Potentially problematic release.


This version of DescripyTor might be problematic. Click here for more details.

Files changed (263) hide show
  1. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/build.py +17 -6
  2. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/cluster.py +8 -5
  3. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/stages.py +2 -0
  4. descripytor-0.4.3/M2_data_extractor/alignment.py +277 -0
  5. descripytor-0.4.3/M2_data_extractor/atom_picker.html +6161 -0
  6. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/data_extractor.py +121 -57
  7. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/descriptor_extractor.py +1 -1
  8. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feather_extractor.py +12 -10
  9. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/build.py +1 -2
  10. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/feature_explorer.html +64 -22
  11. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/scene.js +24 -5
  12. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/template.html +38 -17
  13. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/gaussian_handler.py +2 -2
  14. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/gui_server.py +403 -861
  15. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/ligand_topology.py +223 -223
  16. descripytor-0.4.3/M2_data_extractor/orca_reader.py +120 -0
  17. descripytor-0.4.3/M2_data_extractor/static/align_guide/01_common_part.svg +206 -0
  18. descripytor-0.4.3/M2_data_extractor/static/align_guide/02_complete_rings.svg +51 -0
  19. descripytor-0.4.3/M2_data_extractor/static/align_guide/03_extend_mcs.svg +54 -0
  20. descripytor-0.4.3/M2_data_extractor/static/align_guide/04_coords_proximity_any.svg +51 -0
  21. descripytor-0.4.3/M2_data_extractor/static/align_guide/05_equivalent_atoms.svg +116 -0
  22. descripytor-0.4.3/M2_data_extractor/static/align_guide/06_the_fit.svg +125 -0
  23. descripytor-0.4.3/M2_data_extractor/static/align_guide/guide.json +32 -0
  24. descripytor-0.4.3/M2_data_extractor/static/align_guide/make_guide.py +239 -0
  25. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/theta_explorer/theta_explorer.html +64 -22
  26. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/xtb_singlepoint.py +236 -236
  27. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/xyz_io.py +308 -308
  28. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/batch.py +2 -1
  29. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/build_case.py +94 -16
  30. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/engine.py +15 -1
  31. descripytor-0.4.3/M3_modeler/bassa_explorer/featurize.py +351 -0
  32. descripytor-0.4.3/M3_modeler/bassa_explorer/index.html +2264 -0
  33. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/running.py +61 -61
  34. descripytor-0.4.3/M3_modeler/bassa_explorer/server.py +278 -0
  35. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/settings.py +22 -2
  36. {descripytor-0.3.2 → descripytor-0.4.3}/PKG-INFO +7 -17
  37. {descripytor-0.3.2 → descripytor-0.4.3}/README.md +474 -481
  38. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/__init__.py +5 -5
  39. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/cli.py +755 -644
  40. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/__init__.py +5 -0
  41. descripytor-0.4.3/descripytor/examples/conformaty_rings/README.md +17 -0
  42. descripytor-0.4.3/descripytor/examples/conformaty_rings/stage_order_query.xyz +21 -0
  43. descripytor-0.4.3/descripytor/examples/conformaty_rings/stage_order_ref.xyz +20 -0
  44. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor.egg-info/PKG-INFO +7 -17
  45. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor.egg-info/SOURCES.txt +18 -11
  46. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor.egg-info/requires.txt +0 -4
  47. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor.egg-info/top_level.txt +0 -1
  48. {descripytor-0.3.2 → descripytor-0.4.3}/pyproject.toml +111 -111
  49. descripytor-0.4.3/tests/test_alignment.py +163 -0
  50. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_bassa_explorer.py +67 -0
  51. descripytor-0.4.3/tests/test_gaussian_info.py +30 -0
  52. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_gui_server.py +134 -23
  53. descripytor-0.4.3/tests/test_input_kinds.py +84 -0
  54. descripytor-0.4.3/tests/test_picker_logic.py +123 -0
  55. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_pipeline.py +49 -0
  56. {descripytor-0.3.2 → descripytor-0.4.3}/utils/help_functions.py +10 -13
  57. {descripytor-0.3.2 → descripytor-0.4.3}/utils/mode_viewer.py +8 -2
  58. descripytor-0.3.2/M2_data_extractor/atom_picker.html +0 -5440
  59. descripytor-0.3.2/M2_data_extractor/feature_extraction_gui.html +0 -6682
  60. descripytor-0.3.2/M2_data_extractor/static/3Dmol-min.js +0 -2
  61. descripytor-0.3.2/M3_modeler/bassa_explorer/index.html +0 -884
  62. descripytor-0.3.2/M3_modeler/bassa_explorer/server.py +0 -175
  63. descripytor-0.3.2/MolAlign/MCS_optimization.py +0 -131
  64. descripytor-0.3.2/MolAlign/RDKIT_utils.py +0 -655
  65. descripytor-0.3.2/MolAlign/df_funs.py +0 -99
  66. descripytor-0.3.2/MolAlign/fmcs.py +0 -2654
  67. descripytor-0.3.2/MolAlign/gaussian_handler.py +0 -193
  68. descripytor-0.3.2/MolAlign/loss.py +0 -19
  69. descripytor-0.3.2/MolAlign/renumbering.py +0 -821
  70. descripytor-0.3.2/MolAlign/rotations.py +0 -14
  71. descripytor-0.3.2/utils/__init__.py +0 -0
  72. {descripytor-0.3.2 → descripytor-0.4.3}/Getting_started_with_examples/descriptor_extraction_toolkit/config_example.json +0 -0
  73. {descripytor-0.3.2 → descripytor-0.4.3}/Getting_started_with_examples/input_example.json +0 -0
  74. {descripytor-0.3.2 → descripytor-0.4.3}/LICENSE +0 -0
  75. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/__init__.py +0 -0
  76. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/esp_grid_driver.py +0 -0
  77. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/main.py +0 -0
  78. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/outwards_senders.py +0 -0
  79. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/__init__.py +0 -0
  80. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/adopt.py +0 -0
  81. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/checks.py +0 -0
  82. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/examples/phosphines.csv +0 -0
  83. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/examples/protocol_bgu.json +0 -0
  84. {descripytor-0.3.2 → descripytor-0.4.3}/M1_pre_calculations/pipeline/protocol.py +0 -0
  85. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/__init__.py +0 -0
  86. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/_folder_dialog.py +0 -0
  87. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/crest_boltzmann_sterimol.py +0 -0
  88. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/cube_sterimol.py +0 -0
  89. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/extractor_utils/__init__.py +0 -0
  90. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/extractor_utils/bond_angle_length_utils.py +0 -0
  91. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/extractor_utils/bond_graph.py +0 -0
  92. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/extractor_utils/dipole_utils.py +0 -0
  93. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/extractor_utils/esp_grid_utils.py +0 -0
  94. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/extractor_utils/sterimol_utils.py +0 -0
  95. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/extractor_utils/vibrations_utils.py +0 -0
  96. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/__init__.py +0 -0
  97. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/data.py +0 -0
  98. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/009_phos.xyz +0 -0
  99. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/009_phos_CuCl.xyz +0 -0
  100. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/026_box_CuCl.xyz +0 -0
  101. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/026_lig.xyz +0 -0
  102. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/032_lig.xyz +0 -0
  103. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/041_lig.xyz +0 -0
  104. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/045_box_CuCl.xyz +0 -0
  105. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/045_lig.xyz +0 -0
  106. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/046_lig.xyz +0 -0
  107. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/055_phos.xyz +0 -0
  108. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/Cu_BOX_008.xyz +0 -0
  109. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/Cu_BOX_018.xyz +0 -0
  110. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/Cu_BOX_020.xyz +0 -0
  111. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/Cu_BOX_047.xyz +0 -0
  112. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/Cu_PyOx_041.xyz +0 -0
  113. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/FL_lig_1.data.json +0 -0
  114. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/FL_lig_1.xyz +0 -0
  115. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/FL_lig_12.data.json +0 -0
  116. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/FL_lig_12.xyz +0 -0
  117. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/FL_lig_13_str_target.data.json +0 -0
  118. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/FL_lig_13_str_target.xyz +0 -0
  119. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/FL_lig_7.data.json +0 -0
  120. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/FL_lig_7.xyz +0 -0
  121. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/Ni_BOX_072.xyz +0 -0
  122. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/Ni_BOX_078.xyz +0 -0
  123. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/benzoic_acid.xyz +0 -0
  124. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-CN.data.json +0 -0
  125. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-CN.xyz +0 -0
  126. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-CN_BA.data.json +0 -0
  127. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-CN_BA.xyz +0 -0
  128. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-OMe.data.json +0 -0
  129. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-OMe.xyz +0 -0
  130. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-OMe_BA.data.json +0 -0
  131. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-OMe_BA.xyz +0 -0
  132. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-SO2Me_BA.data.json +0 -0
  133. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/m-SO2Me_BA.xyz +0 -0
  134. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/naphthyl_BA.data.json +0 -0
  135. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/naphthyl_BA.xyz +0 -0
  136. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/o-Me_BA.data.json +0 -0
  137. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/o-Me_BA.xyz +0 -0
  138. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-CF3.data.json +0 -0
  139. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-CF3.xyz +0 -0
  140. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-CF3_substrate.xyz +0 -0
  141. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-CO2Me_BA.data.json +0 -0
  142. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-CO2Me_BA.xyz +0 -0
  143. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-COMe_BA.data.json +0 -0
  144. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-COMe_BA.xyz +0 -0
  145. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-Cl_BA.data.json +0 -0
  146. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-Cl_BA.xyz +0 -0
  147. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-F_BA.data.json +0 -0
  148. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-F_BA.xyz +0 -0
  149. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-H_BA.data.json +0 -0
  150. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-H_BA.xyz +0 -0
  151. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-NO2.data.json +0 -0
  152. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-NO2.xyz +0 -0
  153. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-OMe_BA.data.json +0 -0
  154. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-OMe_BA.xyz +0 -0
  155. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-Otf.data.json +0 -0
  156. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-Otf.xyz +0 -0
  157. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-Otf_substrate.xyz +0 -0
  158. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-t-Bu_BA.data.json +0 -0
  159. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/fixtures/p-t-Bu_BA.xyz +0 -0
  160. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/gate.js +0 -0
  161. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/gate_pos.js +0 -0
  162. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/make_pos_refs.py +0 -0
  163. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/make_refs.py +0 -0
  164. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/pos_refs.json +0 -0
  165. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/promol.js +0 -0
  166. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/rdkit/RDKit_minimal.js +0 -0
  167. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/rdkit/RDKit_minimal.wasm +0 -0
  168. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/refs.json +0 -0
  169. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/render_presets.js +0 -0
  170. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/smarts.js +0 -0
  171. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/smoke.js +0 -0
  172. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/sterimol.js +0 -0
  173. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_explorer/vbur.js +0 -0
  174. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/feature_packages.py +0 -0
  175. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/metal_complex.py +0 -0
  176. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/morfeus_sterics.py +0 -0
  177. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/placement_search.py +0 -0
  178. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/sterimol_standalone.py +0 -0
  179. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/theta_explorer/__init__.py +0 -0
  180. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/theta_explorer/build.py +0 -0
  181. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/theta_explorer/data.py +0 -0
  182. {descripytor-0.3.2 → descripytor-0.4.3}/M2_data_extractor/theta_explorer/sterimol.js +0 -0
  183. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/__init__.py +0 -0
  184. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/__init__.py +0 -0
  185. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/core_fixed.py +0 -0
  186. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/example.js +0 -0
  187. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/example_cs2.csv +0 -0
  188. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/example_small.csv +0 -0
  189. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/influence_check.py +0 -0
  190. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/read_structures.py +0 -0
  191. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/bassa_explorer/test_page.py +0 -0
  192. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/modeling.py +0 -0
  193. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/modeling_utils.py +0 -0
  194. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/nested_cv.py +0 -0
  195. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/plot.py +0 -0
  196. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/ridge_search.py +0 -0
  197. {descripytor-0.3.2 → descripytor-0.4.3}/M3_modeler/validation.py +0 -0
  198. {descripytor-0.3.2 → descripytor-0.4.3}/MANIFEST.in +0 -0
  199. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/__main__.py +0 -0
  200. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/compat.py +0 -0
  201. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/README.md +0 -0
  202. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/m-CN.feather +0 -0
  203. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/m-COMe.feather +0 -0
  204. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/m-NO2.feather +0 -0
  205. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/m-OMe.feather +0 -0
  206. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/m-cyclo.feather +0 -0
  207. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/o-CN.feather +0 -0
  208. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/outcomes.csv +0 -0
  209. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-CF3.feather +0 -0
  210. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-CHO.feather +0 -0
  211. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-CN.feather +0 -0
  212. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-COOMe.feather +0 -0
  213. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-Cl.feather +0 -0
  214. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-MeO.feather +0 -0
  215. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-NO2.feather +0 -0
  216. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-OMe.feather +0 -0
  217. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/p-Otf.feather +0 -0
  218. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/presets.json +0 -0
  219. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/pyridine-m-OMe.feather +0 -0
  220. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/thiophene.feather +0 -0
  221. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/baptiste_products/unsub.feather +0 -0
  222. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/config_example.json +0 -0
  223. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/README.md +0 -0
  224. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/basic.feather +0 -0
  225. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/input_example.json +0 -0
  226. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/m_Br.feather +0 -0
  227. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/m_Cl.feather +0 -0
  228. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/m_nitro.feather +0 -0
  229. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/o_Br.feather +0 -0
  230. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/o_Cl.feather +0 -0
  231. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/o_F.feather +0 -0
  232. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/p_Cl.feather +0 -0
  233. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/p_F.feather +0 -0
  234. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/p_I.feather +0 -0
  235. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/feather_example/presets.json +0 -0
  236. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor/examples/input_example.json +0 -0
  237. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor.egg-info/dependency_links.txt +0 -0
  238. {descripytor-0.3.2 → descripytor-0.4.3}/descripytor.egg-info/entry_points.txt +0 -0
  239. {descripytor-0.3.2 → descripytor-0.4.3}/setup.cfg +0 -0
  240. {descripytor-0.3.2 → descripytor-0.4.3}/setup.py +0 -0
  241. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_arm_fixes.py +0 -0
  242. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_bond_graph.py +0 -0
  243. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_cli.py +0 -0
  244. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_compat.py +0 -0
  245. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_connectivity.py +0 -0
  246. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_esp_grid.py +0 -0
  247. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_examples.py +0 -0
  248. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_extract_and_model.py +0 -0
  249. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_feature_fixes.py +0 -0
  250. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_metal_complex.py +0 -0
  251. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_metal_complex_frame.py +0 -0
  252. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_ridge_search.py +0 -0
  253. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_sterimol_b1_normal.py +0 -0
  254. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_sterimol_loc_b1.py +0 -0
  255. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_two_csv.py +0 -0
  256. {descripytor-0.3.2 → descripytor-0.4.3}/tests/test_validation.py +0 -0
  257. {descripytor-0.3.2/MolAlign → descripytor-0.4.3/utils}/__init__.py +0 -0
  258. {descripytor-0.3.2 → descripytor-0.4.3}/utils/console.py +0 -0
  259. {descripytor-0.3.2 → descripytor-0.4.3}/utils/constants.py +0 -0
  260. {descripytor-0.3.2 → descripytor-0.4.3}/utils/file_handlers.py +0 -0
  261. {descripytor-0.3.2 → descripytor-0.4.3}/utils/mode_viewer.html +0 -0
  262. {descripytor-0.3.2 → descripytor-0.4.3}/utils/rdkit_utils.py +0 -0
  263. {descripytor-0.3.2 → descripytor-0.4.3}/utils/visualize.py +0 -0
@@ -76,13 +76,17 @@ def _embed(smiles: str, n_conf: int, seed: int, pair=None, pair_range=(2.5, 3.0)
76
76
  mol = Chem.AddHs(mol)
77
77
  params = AllChem.ETKDGv3()
78
78
  params.randomSeed = seed
79
+ pair_held = bool(pair)
79
80
  if pair:
80
81
  from rdkit import DistanceGeometry
81
82
  from rdkit.Chem import rdDistGeom
82
83
  i, j = sorted(pair)
83
84
  bm = rdDistGeom.GetMoleculeBoundsMatrix(mol)
84
85
  bm[i][j], bm[j][i] = pair_range[1], pair_range[0] # upper triangle = max, lower = min
85
- DistanceGeometry.DoTriangleSmoothing(bm)
86
+ if not DistanceGeometry.DoTriangleSmoothing(bm):
87
+ # a rigid ligand (fused biimidazoline) fixes the donor distance outside this range: embed it as it is,
88
+ # and let the bite-angle test decide. Another donor pair must not win just because it is flexible.
89
+ bm = rdDistGeom.GetMoleculeBoundsMatrix(mol); DistanceGeometry.DoTriangleSmoothing(bm); pair_held = False
86
90
  params.SetBoundsMat(bm)
87
91
  params.useExpTorsionAnglePrefs = False # the torsion library holds bipyridine anti over the bound
88
92
  cids = list(AllChem.EmbedMultipleConfs(mol, n_conf, params))
@@ -93,9 +97,9 @@ def _embed(smiles: str, n_conf: int, seed: int, pair=None, pair_range=(2.5, 3.0)
93
97
  energy = {}
94
98
  for cid in cids:
95
99
  ff = AllChem.MMFFGetMoleculeForceField(mol, props, confId=cid) if mmff else AllChem.UFFGetMoleculeForceField(mol, confId=cid)
96
- if pair and mmff:
100
+ if pair_held and mmff:
97
101
  ff.MMFFAddDistanceConstraint(pair[0], pair[1], False, pair_range[0], pair_range[1] - 0.1, 100.0)
98
- elif pair:
102
+ elif pair_held:
99
103
  ff.UFFAddDistanceConstraint(pair[0], pair[1], False, pair_range[0], pair_range[1] - 0.1, 100.0)
100
104
  ff.Minimize(maxIts=2000)
101
105
  energy[cid] = ff.CalcEnergy()
@@ -131,6 +135,7 @@ def _relax_around_metal(mol, cid, m, donors, dists):
131
135
  rw.AddBond(int(d), k, Chem.BondType.SINGLE)
132
136
  cx = rw.GetMol()
133
137
  cx.UpdatePropertyCache(strict=False)
138
+ Chem.FastFindRings(cx) # the stand-in closes a new ring (the chelate); UFF typing needs ring info
134
139
  atom = cx.GetAtomWithIdx(k)
135
140
  atom.SetHybridization(Chem.HybridizationType.SP3)
136
141
  atom.SetNoImplicit(True)
@@ -182,14 +187,16 @@ def _ancillaries(m, d1, d2, ligand, dist, n, target):
182
187
  out = []
183
188
  for u in ideal:
184
189
  best, bestd = u, -1.0
190
+ clear = np.vstack([ligand] + out) # the ancillaries placed so far count too: two tilting Cl ended up 1.1-2.3 A apart
185
191
  perp = np.cross(nrm, u)
186
192
  perp /= np.linalg.norm(perp)
187
193
  for tilt in np.radians([0, 10, 20, 30]):
188
- for phi in np.linspace(0, 2 * np.pi, 12, endpoint=False):
194
+ # square planar: tilt out of the plane only; an in-plane turn walks one Cl into the other's site
195
+ for phi in (np.pi / 2, 3 * np.pi / 2) if n == 2 else np.linspace(0, 2 * np.pi, 12, endpoint=False):
189
196
  axis = np.cos(phi) * nrm + np.sin(phi) * perp
190
197
  v = u * np.cos(tilt) + np.cross(axis, u) * np.sin(tilt)
191
198
  v /= np.linalg.norm(v)
192
- score = min(np.linalg.norm(ligand - (m + v * dist), axis=1).min(), target) - 0.15 * tilt
199
+ score = min(np.linalg.norm(clear - (m + v * dist), axis=1).min(), target) - 0.15 * tilt
193
200
  if score > bestd:
194
201
  best, bestd = v, score
195
202
  if bestd > 0.9 * target:
@@ -297,7 +304,11 @@ def _build_chelate(smiles, b, n_conf, seed):
297
304
  raise BuildError("no bidentate donor pair found")
298
305
  tried = []
299
306
  for i, j in pairs:
300
- mol, confs = _embed(smiles, n_conf, seed, pair=(i, j))
307
+ try:
308
+ mol, confs = _embed(smiles, n_conf, seed, pair=(i, j))
309
+ except BuildError as exc: # this pair cannot chelate: try the next candidate
310
+ tried.append(f"{i + 1}-{j + 1}: {exc}")
311
+ continue
301
312
  sym = [a.GetSymbol() for a in mol.GetAtoms()]
302
313
  n = len(sym)
303
314
  key = {}
@@ -39,13 +39,13 @@ class Remote:
39
39
  def __init__(self, host: str, key: str, timeout: int = 180):
40
40
  self.host, self.key, self.timeout = host, os.path.expanduser(key), timeout
41
41
 
42
- def _ssh(self, command: str) -> str:
42
+ def _ssh(self, command: str, timeout: int | None = None) -> str:
43
43
  cmd = ["ssh", "-n", "-T", "-o", "BatchMode=yes", "-o", "ConnectTimeout=30", "-o", "IdentitiesOnly=yes",
44
44
  "-i", self.key, self.host, command]
45
- r = subprocess.run(cmd, capture_output=True, timeout=self.timeout)
45
+ r = subprocess.run(cmd, capture_output=True, timeout=timeout or self.timeout)
46
46
  return (r.stdout or b"").decode("utf-8", "replace")
47
47
 
48
- def run(self, script: str, tries: int = 3) -> str:
48
+ def run(self, script: str, tries: int = 3, timeout: int | None = None) -> str:
49
49
  """Run a bash script remotely; returns its stdout. Retries when the reply comes back empty
50
50
  or cut short (the end marker is missing). Scripts must be safe to run twice."""
51
51
  payload = base64.b64encode(f"echo {BEGIN}\n{script}\necho {END}\n".encode()).decode()
@@ -53,7 +53,7 @@ class Remote:
53
53
  raise ClusterError("remote script too long for the tcsh login shell; upload it as a file instead")
54
54
  last = ""
55
55
  for _ in range(tries):
56
- last = self._ssh(f"echo {payload} | base64 -d | bash")
56
+ last = self._ssh(f"echo {payload} | base64 -d | bash", timeout)
57
57
  if BEGIN in last and END in last:
58
58
  return last.split(BEGIN, 1)[1].rsplit(END, 1)[0].strip("\n")
59
59
  time.sleep(2)
@@ -125,7 +125,10 @@ def _run_chain(r: Remote, p: Protocol, plan: str, plan_name: str) -> dict:
125
125
  """Upload a plan and submit it. Returns {task: {stage: job id}}. Raises on any qsub failure,
126
126
  reporting what was submitted, and never resubmits blindly."""
127
127
  _put_file(r, p.remote_root, plan_name, plan)
128
- out = r.run(f"bash {p.remote_root}/submit_chain.sh {p.remote_root}/{plan_name}", tries=1)
128
+ # one qsub takes about 2 s on hpcgate, so the reply time grows with the plan: with the default timeout a plan of
129
+ # more than about 30 three-stage molecules was cut off part-way, its job ids lost with the reply
130
+ n_qsub = sum(len(line.split()) - 1 for line in plan.splitlines())
131
+ out = r.run(f"bash {p.remote_root}/submit_chain.sh {p.remote_root}/{plan_name}", tries=1, timeout=max(r.timeout, 60 + 10 * n_qsub))
129
132
  jobs, failed = {}, []
130
133
  for line in out.splitlines():
131
134
  f = line.split()
@@ -257,7 +257,9 @@ print("converged" if converged else "NOT converged", f, flush=True)
257
257
  '''
258
258
 
259
259
  GAUSSIAN_BODY = r"""export g16root=@@G16ROOT@@ GAUSS_SCRDIR="$W"
260
+ set +u # g16.profile reads variables that may be unset (PERLLIB); under set -u the job dies with its marker left at "running"
260
261
  source "$g16root/g16/bsd/g16.profile"
262
+ set -u
261
263
  {
262
264
  printf '%%nprocshared=%s\n%%mem=%sGB\n%%chk=%s.chk\n' @@CORES@@ @@MEM@@ "$NAME"
263
265
  cat <<'ROUTE'
@@ -0,0 +1,277 @@
1
+ """Atom renumbering with Conformaty (github.com/Milo-group/Conformaty): every molecule of a folder numbered like a
2
+ reference, so a descriptor picked on the reference reads the same atom everywhere.
3
+
4
+ The result per molecule is the 1-based {old: new} dict ``Molecule.renumber_atoms`` takes. Conformaty matches the
5
+ largest common substructure, fits the molecule onto the reference and assigns the rest by position, so equivalent
6
+ atoms (the two H of a CH2) are told apart by where they sit. The reference must be the smaller molecule (it is the
7
+ common part); a molecule larger than it keeps its extra atoms after the reference's.
8
+
9
+ from M2_data_extractor.alignment import map_folder, write_renumbered
10
+ results = map_folder(mols, "basic") # mols: a Molecules
11
+ write_renumbered(mols, results, "renumbered")
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ import time
17
+ from concurrent.futures import ProcessPoolExecutor, as_completed
18
+
19
+ INSTALL = "pip install git+https://github.com/Milo-group/Conformaty" # shortcut: a git URL until Conformaty is on PyPI, then the [align] extra
20
+
21
+
22
+ def _conformaty():
23
+ try:
24
+ import conformaty
25
+ except ImportError as exc:
26
+ raise ImportError(f"Atom renumbering needs Conformaty: {INSTALL}") from exc
27
+ return conformaty
28
+
29
+
30
+ def _worker_threads(n):
31
+ import torch
32
+
33
+ torch.set_num_threads(n) # one: the fits are a few dozen atoms, where more threads only add overhead
34
+ _conformaty() # import torch and Conformaty once per process, not on every molecule
35
+
36
+
37
+ _POOL = {"pool": None, "n": 0} # the worker processes, kept between runs: starting one and importing costs ~5 s
38
+
39
+
40
+ def _pool(workers):
41
+ pool = _POOL["pool"]
42
+ if pool is None or _POOL["n"] != workers or getattr(pool, "_broken", False):
43
+ if pool is not None:
44
+ pool.shutdown(wait=False, cancel_futures=True)
45
+ pool = ProcessPoolExecutor(workers, initializer=_worker_threads, initargs=(1,))
46
+ _POOL.update(pool=pool, n=workers)
47
+ return pool
48
+
49
+
50
+ # Conformaty's losses and Kabsch. rmsd is the default here. Since Conformaty starts its fits at the Kabsch pose (d74f074)
51
+ # none stalls on the example set; mixed and sig settle up to 0.5 A off Kabsch on o_Br/o_Cl, where their own losses prefer
52
+ # to be. Before, from its principal-axis seed, mixed and mad stalled about 6 A away on 5 of 9.
53
+ FITS = ("rmsd", "msd", "mixed", "mad", "sig", "kabsch")
54
+
55
+
56
+ def kabsch(moving, fixed, rows):
57
+ """moving, rotated and moved onto fixed by the closed-form least-squares fit of `rows` (the smallest RMSD)."""
58
+ import numpy as np
59
+
60
+ a, b = moving[rows], fixed[rows]
61
+ ca, cb = a.mean(0), b.mean(0)
62
+ u, _s, vt = np.linalg.svd((a - ca).T @ (b - cb))
63
+ return (moving - ca) @ (u @ np.diag([1.0, 1.0, np.sign(np.linalg.det(u @ vt))]) @ vt) + cb
64
+
65
+
66
+ def fit_onto(ref, symbols, coords, fit="rmsd", sigma=30, rows=None):
67
+ """The molecule (symbols, coords in the reference's numbering) fitted onto the reference (symbols, coords) over
68
+ `rows` (0-based; default the heavy atoms holding the reference's element, as Conformaty's own align fits).
69
+ fit: one of FITS, Conformaty's rigid-body optimiser with that loss, or kabsch. Returns the fitted (n, 3) array, or
70
+ the coordinates unmoved if fewer than three rows match."""
71
+ import numpy as np
72
+
73
+ rs, rx = ref
74
+ rx, coords = np.asarray(rx, float), np.asarray(coords, float)
75
+ if rows is None:
76
+ rows = [i for i in range(len(rs)) if symbols[i] == rs[i]]
77
+ heavy = [i for i in rows if rs[i] != "H"]
78
+ rows = heavy if len(heavy) >= 3 else rows
79
+ if len(rows) < 3:
80
+ return coords
81
+ if fit == "kabsch":
82
+ return kabsch(coords, rx, rows)
83
+ cf = _conformaty()
84
+ moved = cf.fit_frames(cf.frame_from_atoms(rs, rx), cf.frame_from_atoms(symbols, coords), {i: i for i in rows},
85
+ loss_type=fit, sigma=sigma)
86
+ return moved.iloc[2:][["X", "Y", "Z"]].to_numpy(float)
87
+
88
+
89
+ def compare_fits(ref, symbols, coords, sigma=30):
90
+ """Every fit (FITS) of one molecule, already in the reference's numbering, onto the reference: the RMSD over the
91
+ positions with the reference's element and over its heavy atoms, how many of them end within 0.25 A, the seconds,
92
+ and the fitted xyz. Kabsch has the smallest RMSD by construction; mixed favours atoms in near-exact overlap."""
93
+ import numpy as np
94
+
95
+ rs, rx = ref[0], np.asarray(ref[1], float)
96
+ same = [i for i in range(len(rs)) if symbols[i] == rs[i]]
97
+ heavy = [i for i in same if rs[i] != "H"]
98
+ out = []
99
+ for fit in FITS:
100
+ t0 = time.perf_counter()
101
+ xyz = fit_onto(ref, symbols, coords, fit, sigma)
102
+ d = np.linalg.norm(xyz[same] - rx[same], axis=1)
103
+ out.append({"fit": fit, "rmsd": float(np.sqrt((d ** 2).mean())) if same else None,
104
+ "rmsd_heavy": float(np.sqrt((np.linalg.norm(xyz[heavy] - rx[heavy], axis=1) ** 2).mean())) if heavy else None,
105
+ "within": int((d < 0.25).sum()), "n": len(same), "seconds": time.perf_counter() - t0,
106
+ "xyz": f"{len(symbols)}\nfitted ({fit})\n" + "\n".join(
107
+ f"{s} {x:.6f} {y:.6f} {z:.6f}" for s, (x, y, z) in zip(symbols, xyz))})
108
+ return out
109
+
110
+
111
+ def map_atoms(ref, query, anchors=None, stages=None, match_charge=False, fit="rmsd", sigma=30, fit_atoms=None):
112
+ """Number one molecule like the reference. ref, query: (symbols, coords) of each.
113
+
114
+ anchors: 1-based (reference atom, query atom) pairs that must correspond. stages: Conformaty's middle stages in
115
+ order (complete_rings, extend_mcs, coords_proximity_any; default all three). fit, sigma: the rigid-body fit of
116
+ the overlay (FITS); the numbering keeps Conformaty's own internal fit. fit_atoms: 1-based reference atoms the
117
+ overlay is fitted on (default its heavy atoms with the reference's element, as Conformaty's align).
118
+ Returns plain values (they cross a process boundary): old_to_new {old: new} (1-based), stage (the Conformaty stage
119
+ that matched each atom, in the new order), xyz (the molecule fitted onto the reference, new order), differs (reference
120
+ positions holding another element), rmsd over the positions with the same element (rmsd_kabsch: the same for
121
+ the Kabsch fit, the least-squares optimum, to tell a stalled fit), seconds.
122
+ """
123
+ import numpy as np
124
+
125
+ cf = _conformaty()
126
+ (rs, rx), (qs, qx) = ref, query
127
+ if len(qs) < len(rs):
128
+ raise ValueError(f"it has fewer atoms ({len(qs)}) than the reference ({len(rs)}): choose the smaller molecule "
129
+ "as the reference")
130
+ t0 = time.perf_counter()
131
+ state = cf.map_frames(cf.frame_from_atoms(rs, rx), cf.frame_from_atoms(qs, qx),
132
+ anchors=[(r - 1, q - 1) for r, q in anchors] if anchors else None,
133
+ stage_order=stages, match_formal_charge=match_charge)
134
+ order = cf.query_order(state)
135
+ found = {q: entry.name for entry in state.log for q in entry.added}
136
+ syms = [str(qs[q]) for q in order]
137
+ same = [i for i in range(len(rs)) if syms[i] == rs[i]]
138
+ rows = [a - 1 for a in fit_atoms if a - 1 in same] if fit_atoms else None
139
+ rx = np.asarray(rx, float)
140
+ moving = np.asarray(qx, float)[order]
141
+
142
+ def dev(xyz):
143
+ return float(np.sqrt(((xyz[same] - rx[same]) ** 2).sum(1).mean())) if same else None
144
+
145
+ fitted = fit_onto((rs, rx), syms, moving, fit, sigma, rows)
146
+ rmsd = dev(fitted)
147
+ best = rmsd if fit == "kabsch" else dev(fit_onto((rs, rx), syms, moving, "kabsch", rows=rows))
148
+ return {
149
+ "old_to_new": {int(q) + 1: k + 1 for k, q in enumerate(order)},
150
+ "stage": [found.get(q, "") for q in order],
151
+ "xyz": f"{len(syms)}\nfitted onto the reference\n" + "\n".join(
152
+ f"{s} {x:.6f} {y:.6f} {z:.6f}" for s, (x, y, z) in zip(syms, fitted)),
153
+ "differs": [i + 1 for i in range(len(rs)) if syms[i] != rs[i]],
154
+ "rmsd": rmsd,
155
+ "rmsd_kabsch": best,
156
+ "fit": fit,
157
+ "seconds": time.perf_counter() - t0,
158
+ }
159
+
160
+
161
+ def _atoms(mol):
162
+ x = mol.xyz_df
163
+ return x["atom"].astype(str).tolist(), x[["x", "y", "z"]].to_numpy(float).tolist()
164
+
165
+
166
+ def map_folder(mols, reference, anchors=None, stages=None, match_charge=False, workers=None, progress=None,
167
+ cancelled=lambda: False, fit="rmsd", sigma=30, fit_atoms=None, isolate=False, only=None):
168
+ """Number every molecule of a Molecules like `reference` (one of its names). anchors: {name: [(ref atom, atom)]}.
169
+
170
+ workers > 1 runs that many molecules at once in separate processes, the cores shared between them; 1 (the default)
171
+ runs them here, one after the other, unless isolate (a server: RDKit's substructure search holds the interpreter
172
+ lock, which would stall its other threads). progress(event) gets one dict per molecule as it finishes; cancelled()
173
+ is asked between molecules. Returns {name: result of map_atoms, or {"error": text}};
174
+ the reference itself maps onto itself unchanged.
175
+ """
176
+ _conformaty()
177
+ by = {m.molecule_name: m for m in mols.molecules}
178
+ if reference not in by:
179
+ raise ValueError(f"no molecule named {reference!r} in this folder")
180
+ ref = _atoms(by[reference])
181
+ n = len(ref[0])
182
+ results = {reference: {"old_to_new": {i: i for i in range(1, n + 1)}, "stage": ["reference"] * n, "differs": [],
183
+ "rmsd": 0.0, "seconds": 0.0, "xyz": None, "reference": True}}
184
+ todo = [name for name in by if name != reference and (only is None or name in only)] # only: these names, again
185
+ workers = max(1, min(len(todo), workers or 1))
186
+ if progress:
187
+ progress({"event": "start", "n": len(todo), "names": todo, "reference": reference, "workers": workers})
188
+ if fit not in FITS:
189
+ raise ValueError(f"fit must be one of {', '.join(FITS)}")
190
+ args = {name: (ref, _atoms(by[name]), (anchors or {}).get(name), stages, match_charge, fit, sigma, fit_atoms)
191
+ for name in todo}
192
+
193
+ def done(i, name, run):
194
+ try:
195
+ results[name] = run()
196
+ except Exception as exc: # one molecule that cannot be mapped does not stop the folder
197
+ results[name] = {"error": f"{type(exc).__name__}: {exc}"}
198
+ if progress:
199
+ progress({"event": "molecule", "i": i, "n": len(todo), "name": name, **_summary(results[name]),
200
+ "result": results[name]})
201
+
202
+ if workers == 1 and not isolate:
203
+ for i, name in enumerate(todo, 1):
204
+ if cancelled():
205
+ break
206
+ done(i, name, lambda: map_atoms(*args[name]))
207
+ return results
208
+ import threading
209
+
210
+ pool = _pool(workers)
211
+ # the pool outlives the run, so the next one starts warm; Stop ends its processes and the next run starts new ones
212
+ jobs = {pool.submit(map_atoms, *args[name]): name for name in todo}
213
+
214
+ def watch(): # one molecule can take minutes: Stop ends its process instead of waiting
215
+ while not all(j.done() for j in jobs):
216
+ if cancelled():
217
+ for other in jobs:
218
+ other.cancel()
219
+ for proc in list(getattr(pool, "_processes", {}).values()):
220
+ proc.terminate()
221
+ _POOL["pool"] = None
222
+ return
223
+ time.sleep(0.3)
224
+
225
+ threading.Thread(target=watch, daemon=True).start()
226
+ for i, job in enumerate(as_completed(jobs), 1):
227
+ if cancelled():
228
+ break
229
+ done(i, jobs[job], job.result)
230
+ return results
231
+
232
+
233
+ def _summary(result):
234
+ if "error" in result:
235
+ return {"error": result["error"]}
236
+ return {"moved": sum(o != n for o, n in result["old_to_new"].items()), "differs": result["differs"],
237
+ "rmsd": result["rmsd"], "rmsd_kabsch": result.get("rmsd_kabsch"), "seconds": result["seconds"]}
238
+
239
+
240
+ def write_renumbered(mols, results, out_dir, reference=""):
241
+ """Write each mapped molecule, renumbered, to out_dir/<name>.feather (the names unchanged, so outcomes.csv still
242
+ joins; it is copied along), and renumbering.json: the {old: new} dict of every molecule. Molecules without one
243
+ (failed) are not written. Returns (written names, skipped names)."""
244
+ import copy
245
+ import json
246
+ import shutil
247
+
248
+ from M2_data_extractor.data_extractor import _molecules_folder_files
249
+
250
+ os.makedirs(out_dir, exist_ok=True)
251
+ folder, files = _molecules_folder_files(mols.molecules_path)
252
+ xyz_in = {os.path.splitext(f)[0] for f, _s, _z in files if f.lower().endswith(".xyz")}
253
+ written, skipped = [], []
254
+ for mol in mols.molecules:
255
+ result = results.get(mol.molecule_name) or {}
256
+ if "old_to_new" not in result:
257
+ skipped.append(mol.molecule_name)
258
+ continue
259
+ m = copy.deepcopy(mol)
260
+ if mol.molecule_name in xyz_in: # coordinates stay coordinates; .log, .out and .json become .feather
261
+ m.renumber_atoms(result["old_to_new"], verbose=False, save_feather=False)
262
+ x = m.xyz_df
263
+ with open(os.path.join(out_dir, f"{mol.molecule_name}.xyz"), "w", encoding="utf-8") as fh:
264
+ fh.write(f"{len(x)}\nrenumbered like {reference}\n" + "".join(
265
+ f"{a} {float(px):.6f} {float(py):.6f} {float(pz):.6f}\n" for a, px, py, pz in x[["atom", "x", "y", "z"]].itertuples(index=False)))
266
+ else:
267
+ m.renumber_atoms(result["old_to_new"], verbose=False, save_feather=True,
268
+ out_file=os.path.join(out_dir, f"{mol.molecule_name}.feather"))
269
+ written.append(mol.molecule_name)
270
+ outcomes = os.path.join(mols.molecules_path, "outcomes.csv")
271
+ if os.path.isfile(outcomes):
272
+ shutil.copy(outcomes, out_dir)
273
+ keep = ("old_to_new", "stage", "differs", "rmsd", "rmsd_kabsch", "fit", "seconds", "error")
274
+ with open(os.path.join(out_dir, "renumbering.json"), "w", encoding="utf-8") as fh:
275
+ json.dump({"reference": reference, "folder": mols.molecules_path,
276
+ "molecules": {k: {f: v[f] for f in keep if f in v} for k, v in results.items()}}, fh, indent=1)
277
+ return written, skipped