MultiOptPy 1.20.2__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 (246) hide show
  1. multioptpy/Calculator/__init__.py +0 -0
  2. multioptpy/Calculator/ase_calculation_tools.py +424 -0
  3. multioptpy/Calculator/ase_tools/__init__.py +0 -0
  4. multioptpy/Calculator/ase_tools/fairchem.py +28 -0
  5. multioptpy/Calculator/ase_tools/gamess.py +19 -0
  6. multioptpy/Calculator/ase_tools/gaussian.py +165 -0
  7. multioptpy/Calculator/ase_tools/mace.py +28 -0
  8. multioptpy/Calculator/ase_tools/mopac.py +19 -0
  9. multioptpy/Calculator/ase_tools/nwchem.py +31 -0
  10. multioptpy/Calculator/ase_tools/orca.py +22 -0
  11. multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
  12. multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
  13. multioptpy/Calculator/emt_calculation_tools.py +458 -0
  14. multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
  15. multioptpy/Calculator/lj_calculation_tools.py +314 -0
  16. multioptpy/Calculator/psi4_calculation_tools.py +334 -0
  17. multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
  18. multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
  19. multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
  20. multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
  21. multioptpy/Calculator/tblite_calculation_tools.py +352 -0
  22. multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
  23. multioptpy/Constraint/__init__.py +0 -0
  24. multioptpy/Constraint/constraint_condition.py +834 -0
  25. multioptpy/Coordinate/__init__.py +0 -0
  26. multioptpy/Coordinate/polar_coordinate.py +199 -0
  27. multioptpy/Coordinate/redundant_coordinate.py +638 -0
  28. multioptpy/IRC/__init__.py +0 -0
  29. multioptpy/IRC/converge_criteria.py +28 -0
  30. multioptpy/IRC/dvv.py +544 -0
  31. multioptpy/IRC/euler.py +439 -0
  32. multioptpy/IRC/hpc.py +564 -0
  33. multioptpy/IRC/lqa.py +540 -0
  34. multioptpy/IRC/modekill.py +662 -0
  35. multioptpy/IRC/rk4.py +579 -0
  36. multioptpy/Interpolation/__init__.py +0 -0
  37. multioptpy/Interpolation/adaptive_interpolation.py +283 -0
  38. multioptpy/Interpolation/binomial_interpolation.py +179 -0
  39. multioptpy/Interpolation/geodesic_interpolation.py +785 -0
  40. multioptpy/Interpolation/interpolation.py +156 -0
  41. multioptpy/Interpolation/linear_interpolation.py +473 -0
  42. multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
  43. multioptpy/Interpolation/spline_interpolation.py +353 -0
  44. multioptpy/MD/__init__.py +0 -0
  45. multioptpy/MD/thermostat.py +185 -0
  46. multioptpy/MEP/__init__.py +0 -0
  47. multioptpy/MEP/pathopt_bneb_force.py +443 -0
  48. multioptpy/MEP/pathopt_dmf_force.py +448 -0
  49. multioptpy/MEP/pathopt_dneb_force.py +130 -0
  50. multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
  51. multioptpy/MEP/pathopt_gpneb_force.py +512 -0
  52. multioptpy/MEP/pathopt_lup_force.py +113 -0
  53. multioptpy/MEP/pathopt_neb_force.py +225 -0
  54. multioptpy/MEP/pathopt_nesb_force.py +205 -0
  55. multioptpy/MEP/pathopt_om_force.py +153 -0
  56. multioptpy/MEP/pathopt_qsm_force.py +174 -0
  57. multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
  58. multioptpy/ModelFunction/__init__.py +7 -0
  59. multioptpy/ModelFunction/avoiding_model_function.py +29 -0
  60. multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
  61. multioptpy/ModelFunction/conical_model_function.py +26 -0
  62. multioptpy/ModelFunction/opt_meci.py +50 -0
  63. multioptpy/ModelFunction/opt_mesx.py +47 -0
  64. multioptpy/ModelFunction/opt_mesx_2.py +49 -0
  65. multioptpy/ModelFunction/seam_model_function.py +27 -0
  66. multioptpy/ModelHessian/__init__.py +0 -0
  67. multioptpy/ModelHessian/approx_hessian.py +147 -0
  68. multioptpy/ModelHessian/calc_params.py +227 -0
  69. multioptpy/ModelHessian/fischer.py +236 -0
  70. multioptpy/ModelHessian/fischerd3.py +360 -0
  71. multioptpy/ModelHessian/fischerd4.py +398 -0
  72. multioptpy/ModelHessian/gfn0xtb.py +633 -0
  73. multioptpy/ModelHessian/gfnff.py +709 -0
  74. multioptpy/ModelHessian/lindh.py +165 -0
  75. multioptpy/ModelHessian/lindh2007d2.py +707 -0
  76. multioptpy/ModelHessian/lindh2007d3.py +822 -0
  77. multioptpy/ModelHessian/lindh2007d4.py +1030 -0
  78. multioptpy/ModelHessian/morse.py +106 -0
  79. multioptpy/ModelHessian/schlegel.py +144 -0
  80. multioptpy/ModelHessian/schlegeld3.py +322 -0
  81. multioptpy/ModelHessian/schlegeld4.py +559 -0
  82. multioptpy/ModelHessian/shortrange.py +346 -0
  83. multioptpy/ModelHessian/swartd2.py +496 -0
  84. multioptpy/ModelHessian/swartd3.py +706 -0
  85. multioptpy/ModelHessian/swartd4.py +918 -0
  86. multioptpy/ModelHessian/tshess.py +40 -0
  87. multioptpy/Optimizer/QHAdam.py +61 -0
  88. multioptpy/Optimizer/__init__.py +0 -0
  89. multioptpy/Optimizer/abc_fire.py +83 -0
  90. multioptpy/Optimizer/adabelief.py +58 -0
  91. multioptpy/Optimizer/adabound.py +68 -0
  92. multioptpy/Optimizer/adadelta.py +65 -0
  93. multioptpy/Optimizer/adaderivative.py +56 -0
  94. multioptpy/Optimizer/adadiff.py +68 -0
  95. multioptpy/Optimizer/adafactor.py +70 -0
  96. multioptpy/Optimizer/adam.py +65 -0
  97. multioptpy/Optimizer/adamax.py +62 -0
  98. multioptpy/Optimizer/adamod.py +83 -0
  99. multioptpy/Optimizer/adamw.py +65 -0
  100. multioptpy/Optimizer/adiis.py +523 -0
  101. multioptpy/Optimizer/afire_neb.py +282 -0
  102. multioptpy/Optimizer/block_hessian_update.py +709 -0
  103. multioptpy/Optimizer/c2diis.py +491 -0
  104. multioptpy/Optimizer/component_wise_scaling.py +405 -0
  105. multioptpy/Optimizer/conjugate_gradient.py +82 -0
  106. multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
  107. multioptpy/Optimizer/coordinate_locking.py +405 -0
  108. multioptpy/Optimizer/dic_rsirfo.py +1015 -0
  109. multioptpy/Optimizer/ediis.py +417 -0
  110. multioptpy/Optimizer/eve.py +76 -0
  111. multioptpy/Optimizer/fastadabelief.py +61 -0
  112. multioptpy/Optimizer/fire.py +77 -0
  113. multioptpy/Optimizer/fire2.py +249 -0
  114. multioptpy/Optimizer/fire_neb.py +92 -0
  115. multioptpy/Optimizer/gan_step.py +486 -0
  116. multioptpy/Optimizer/gdiis.py +609 -0
  117. multioptpy/Optimizer/gediis.py +203 -0
  118. multioptpy/Optimizer/geodesic_step.py +433 -0
  119. multioptpy/Optimizer/gpmin.py +633 -0
  120. multioptpy/Optimizer/gpr_step.py +364 -0
  121. multioptpy/Optimizer/gradientdescent.py +78 -0
  122. multioptpy/Optimizer/gradientdescent_neb.py +52 -0
  123. multioptpy/Optimizer/hessian_update.py +433 -0
  124. multioptpy/Optimizer/hybrid_rfo.py +998 -0
  125. multioptpy/Optimizer/kdiis.py +625 -0
  126. multioptpy/Optimizer/lars.py +21 -0
  127. multioptpy/Optimizer/lbfgs.py +253 -0
  128. multioptpy/Optimizer/lbfgs_neb.py +355 -0
  129. multioptpy/Optimizer/linesearch.py +236 -0
  130. multioptpy/Optimizer/lookahead.py +40 -0
  131. multioptpy/Optimizer/nadam.py +64 -0
  132. multioptpy/Optimizer/newton.py +200 -0
  133. multioptpy/Optimizer/prodigy.py +70 -0
  134. multioptpy/Optimizer/purtubation.py +16 -0
  135. multioptpy/Optimizer/quickmin_neb.py +245 -0
  136. multioptpy/Optimizer/radam.py +75 -0
  137. multioptpy/Optimizer/rfo_neb.py +302 -0
  138. multioptpy/Optimizer/ric_rfo.py +842 -0
  139. multioptpy/Optimizer/rl_step.py +627 -0
  140. multioptpy/Optimizer/rmspropgrave.py +65 -0
  141. multioptpy/Optimizer/rsirfo.py +1647 -0
  142. multioptpy/Optimizer/rsprfo.py +1056 -0
  143. multioptpy/Optimizer/sadam.py +60 -0
  144. multioptpy/Optimizer/samsgrad.py +63 -0
  145. multioptpy/Optimizer/tr_lbfgs.py +678 -0
  146. multioptpy/Optimizer/trim.py +273 -0
  147. multioptpy/Optimizer/trust_radius.py +207 -0
  148. multioptpy/Optimizer/trust_radius_neb.py +121 -0
  149. multioptpy/Optimizer/yogi.py +60 -0
  150. multioptpy/OtherMethod/__init__.py +0 -0
  151. multioptpy/OtherMethod/addf.py +1150 -0
  152. multioptpy/OtherMethod/dimer.py +895 -0
  153. multioptpy/OtherMethod/elastic_image_pair.py +629 -0
  154. multioptpy/OtherMethod/modelfunction.py +456 -0
  155. multioptpy/OtherMethod/newton_traj.py +454 -0
  156. multioptpy/OtherMethod/twopshs.py +1095 -0
  157. multioptpy/PESAnalyzer/__init__.py +0 -0
  158. multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
  159. multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
  160. multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
  161. multioptpy/PESAnalyzer/pca_analysis.py +314 -0
  162. multioptpy/Parameters/__init__.py +0 -0
  163. multioptpy/Parameters/atomic_mass.py +20 -0
  164. multioptpy/Parameters/atomic_number.py +22 -0
  165. multioptpy/Parameters/covalent_radii.py +44 -0
  166. multioptpy/Parameters/d2.py +61 -0
  167. multioptpy/Parameters/d3.py +63 -0
  168. multioptpy/Parameters/d4.py +103 -0
  169. multioptpy/Parameters/dreiding.py +34 -0
  170. multioptpy/Parameters/gfn0xtb_param.py +137 -0
  171. multioptpy/Parameters/gfnff_param.py +315 -0
  172. multioptpy/Parameters/gnb.py +104 -0
  173. multioptpy/Parameters/parameter.py +22 -0
  174. multioptpy/Parameters/uff.py +72 -0
  175. multioptpy/Parameters/unit_values.py +20 -0
  176. multioptpy/Potential/AFIR_potential.py +55 -0
  177. multioptpy/Potential/LJ_repulsive_potential.py +345 -0
  178. multioptpy/Potential/__init__.py +0 -0
  179. multioptpy/Potential/anharmonic_keep_potential.py +28 -0
  180. multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
  181. multioptpy/Potential/electrostatic_potential.py +69 -0
  182. multioptpy/Potential/flux_potential.py +30 -0
  183. multioptpy/Potential/gaussian_potential.py +101 -0
  184. multioptpy/Potential/idpp.py +516 -0
  185. multioptpy/Potential/keep_angle_potential.py +146 -0
  186. multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
  187. multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
  188. multioptpy/Potential/keep_potential.py +99 -0
  189. multioptpy/Potential/mechano_force_potential.py +74 -0
  190. multioptpy/Potential/nanoreactor_potential.py +52 -0
  191. multioptpy/Potential/potential.py +896 -0
  192. multioptpy/Potential/spacer_model_potential.py +221 -0
  193. multioptpy/Potential/switching_potential.py +258 -0
  194. multioptpy/Potential/universal_potential.py +34 -0
  195. multioptpy/Potential/value_range_potential.py +36 -0
  196. multioptpy/Potential/void_point_potential.py +25 -0
  197. multioptpy/SQM/__init__.py +0 -0
  198. multioptpy/SQM/sqm1/__init__.py +0 -0
  199. multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
  200. multioptpy/SQM/sqm2/__init__.py +0 -0
  201. multioptpy/SQM/sqm2/calc_tools.py +95 -0
  202. multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
  203. multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
  204. multioptpy/SQM/sqm2/sqm2_core.py +303 -0
  205. multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
  206. multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
  207. multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
  208. multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
  209. multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
  210. multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
  211. multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
  212. multioptpy/Thermo/__init__.py +0 -0
  213. multioptpy/Thermo/normal_mode_analyzer.py +865 -0
  214. multioptpy/Utils/__init__.py +0 -0
  215. multioptpy/Utils/bond_connectivity.py +264 -0
  216. multioptpy/Utils/calc_tools.py +884 -0
  217. multioptpy/Utils/oniom.py +96 -0
  218. multioptpy/Utils/pbc.py +48 -0
  219. multioptpy/Utils/riemann_curvature.py +208 -0
  220. multioptpy/Utils/symmetry_analyzer.py +482 -0
  221. multioptpy/Visualization/__init__.py +0 -0
  222. multioptpy/Visualization/visualization.py +156 -0
  223. multioptpy/WFAnalyzer/MO_analysis.py +104 -0
  224. multioptpy/WFAnalyzer/__init__.py +0 -0
  225. multioptpy/Wrapper/__init__.py +0 -0
  226. multioptpy/Wrapper/autots.py +1239 -0
  227. multioptpy/Wrapper/ieip_wrapper.py +93 -0
  228. multioptpy/Wrapper/md_wrapper.py +92 -0
  229. multioptpy/Wrapper/neb_wrapper.py +94 -0
  230. multioptpy/Wrapper/optimize_wrapper.py +76 -0
  231. multioptpy/__init__.py +5 -0
  232. multioptpy/entrypoints.py +916 -0
  233. multioptpy/fileio.py +660 -0
  234. multioptpy/ieip.py +340 -0
  235. multioptpy/interface.py +1086 -0
  236. multioptpy/irc.py +529 -0
  237. multioptpy/moleculardynamics.py +432 -0
  238. multioptpy/neb.py +1267 -0
  239. multioptpy/optimization.py +1553 -0
  240. multioptpy/optimizer.py +709 -0
  241. multioptpy-1.20.2.dist-info/METADATA +438 -0
  242. multioptpy-1.20.2.dist-info/RECORD +246 -0
  243. multioptpy-1.20.2.dist-info/WHEEL +5 -0
  244. multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
  245. multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
  246. multioptpy-1.20.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,916 @@
1
+ import sys
2
+ import os
3
+ import shutil
4
+ import datetime
5
+ import json
6
+ import argparse
7
+ import random
8
+ import numpy as np
9
+ import itertools
10
+
11
+ # --- 1. Core Imports (Relying on pip installation) ---
12
+ import multioptpy
13
+ from multioptpy.Parameters.unit_values import UnitValueLib
14
+ from multioptpy.Utils import calc_tools
15
+ from multioptpy.Wrapper.autots import AutoTSWorkflow
16
+ from multioptpy.Wrapper.autots import AutoTSWorkflow_v2
17
+
18
+ # --- 2. Entry Point Functions (Matching pyproject.toml) ---
19
+
20
+ def run_optmain():
21
+ """ Entry point for the main geometry optimization script (optmain.py). """
22
+ parser = multioptpy.interface.init_parser()
23
+ args = multioptpy.interface.optimizeparser(parser)
24
+ bpa = multioptpy.optimization.Optimize(args)
25
+ bpa.run()
26
+
27
+
28
+ def run_ieipmain():
29
+ """ Entry point for the iEIP calculation script (ieipmain.py). """
30
+ parser = multioptpy.interface.init_parser()
31
+ args = multioptpy.interface.ieipparser(parser)
32
+ iEIP = multioptpy.ieip.iEIP(args)
33
+ iEIP.run()
34
+
35
+
36
+
37
+ def run_mdmain():
38
+ """ Entry point for the molecular dynamics script (mdmain.py). """
39
+ parser = multioptpy.interface.init_parser()
40
+ args = multioptpy.interface.mdparser(parser)
41
+ MD = multioptpy.moleculardynamics.MD(args)
42
+ MD.run()
43
+
44
+
45
+
46
+ def run_nebmain():
47
+ """ Entry point for the Nudged Elastic Band (NEB) calculation script (nebmain.py). """
48
+ parser = multioptpy.interface.init_parser()
49
+ args = multioptpy.interface.nebparser(parser)
50
+ NEB = multioptpy.neb.NEB(args)
51
+ NEB.run()
52
+
53
+
54
+
55
+ def run_relaxedscan():
56
+ """ Entry point for the relaxed scan script (relaxed_scan.py). """
57
+ # When you use psi4 with this script, segmentation fault may occur. Thus, I recommend to use pyscf, tblite and so on with the following script.
58
+
59
+ def relaxed_scan_perser(parser):
60
+ parser.add_argument("-nsample", "--number_of_samples", type=int, default=10, help='the number of sampling relaxed scan coordinates')
61
+ parser.add_argument("-scan", "--scan_tgt", nargs="*", type=str, help='scan target (ex.) [[bond, angle, or dihedral etc.] [atom_num] [(value_1(ex. 1.0 ang.)),(value_2(ex. 1.5 ang.))] ...] ', default=None)
62
+ parser.add_argument("-fo", "--first_only", action="store_true", help='use only input structure for relax scan ')
63
+ return parser
64
+
65
+
66
+ def num_parse(numbers):
67
+ sub_list = []
68
+
69
+ sub_tmp_list = numbers.split(",")
70
+ for sub in sub_tmp_list:
71
+ if "-" in sub:
72
+ for j in range(int(sub.split("-")[0]),int(sub.split("-")[1])+1):
73
+ sub_list.append(j)
74
+ else:
75
+ sub_list.append(int(sub))
76
+ return sub_list
77
+
78
+
79
+ def save_xyz_file(coord_list, element_list, file_name, additional_name, directory):
80
+ no_ext_file_name = os.path.splitext(file_name)[0]
81
+ sample_file_name = no_ext_file_name+"_RelaxedScan_"+str(additional_name)+".xyz"
82
+ with open(directory + sample_file_name, 'w') as f:
83
+ f.write(str(len(coord_list))+"\n")
84
+ f.write("RelaxedScan_"+str(additional_name)+"\n")
85
+ for i in range(len(coord_list)):
86
+ f.write(element_list[i]+" "+str(coord_list[i][0])+" "+str(coord_list[i][1])+" "+str(coord_list[i][2])+"\n")
87
+
88
+ return sample_file_name
89
+
90
+ def make_scan_tgt(scan_list):
91
+ scan_tgt_list = []
92
+ atom_num_list = []
93
+ scan_range_list = []
94
+ for i in range(int(len(scan_list)/3)):
95
+ scan = scan_list[i*3:i*3+3]
96
+ scan_type = str(scan[0])
97
+ atom_num_list.append(scan[1])
98
+ tmp_scan_tgt = scan[2].split(",")
99
+ scan_tgt_list.append(scan_type)
100
+ scan_range_list.append([float(tmp_scan_tgt[0]), float(tmp_scan_tgt[1])])
101
+ return scan_tgt_list, atom_num_list, scan_range_list
102
+
103
+
104
+
105
+ parser = multioptpy.interface.init_parser()
106
+ parser = relaxed_scan_perser(parser)
107
+ args = multioptpy.interface.optimizeparser(parser)
108
+ first_only_flag = args.first_only
109
+ date = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
110
+ directory = os.path.splitext(args.INPUT)[0]+"_RScan_"+date+"/"
111
+ os.makedirs(directory, exist_ok=True)
112
+ ene_profile_filepath = directory + os.path.splitext(args.INPUT)[0]+"_scan_energy_profile_"+date+".csv"
113
+
114
+ scan_part = int(args.number_of_samples)
115
+ scan_tgt_list, atom_num_list, scan_range_list = make_scan_tgt(args.scan_tgt)
116
+
117
+ with open(args.INPUT, 'r') as f:
118
+ words = f.read().splitlines()
119
+
120
+
121
+ print("Start relaxed scan .... ")
122
+ print("Scan target: ", scan_tgt_list, atom_num_list)
123
+ print("Scan range: ", scan_range_list)
124
+ scan_job_list = np.array([np.linspace(start, end, scan_part) for start, end in scan_range_list]).T
125
+
126
+
127
+ original_input_file = args.INPUT
128
+ original_args = args
129
+
130
+ with open(ene_profile_filepath, "w") as f:
131
+ f.write(str(",".join(scan_tgt_list))+",energy,bias_energy\n")
132
+ tmp_atom_num_list = [str(i).replace(",", "_") for i in atom_num_list]
133
+ f.write(str(",".join(tmp_atom_num_list))+",(hartree),(hartree)\n")
134
+
135
+
136
+ shutil.copyfile(original_input_file, directory + os.path.splitext(original_input_file)[0]+"_RelaxedScan_0.xyz")
137
+ shutil.copyfile(original_input_file, os.path.splitext(original_input_file)[0]+"_RelaxedScan_0.xyz")
138
+ init_input_file = os.path.splitext(original_input_file)[0]+"_RelaxedScan_0.xyz"
139
+ args.INPUT = init_input_file
140
+
141
+ with open(directory + "input.txt", 'w') as f:
142
+ f.write(str(vars(args)))
143
+
144
+ for i in range(scan_part):
145
+ print("scan type(s) : ", scan_tgt_list)
146
+ print("scan atom(s) : ", atom_num_list)
147
+ print("scan value(s) : ", scan_job_list[i])
148
+ args.projection_constrain = []
149
+ for scan_tgt, atom_num, scan_value in zip(scan_tgt_list, atom_num_list, scan_job_list[i]):
150
+ args.projection_constrain.extend(["manual", scan_tgt, atom_num, str(scan_value)])
151
+
152
+ bpa = multioptpy.optimization.Optimize(args)
153
+ bpa.run()
154
+
155
+ opted_geometry = bpa.final_geometry * UnitValueLib().bohr2angstroms # get optimized geometry
156
+ element_list = bpa.element_list
157
+ energy = bpa.final_energy
158
+ bias_energy = bpa.final_bias_energy
159
+
160
+ next_input_file_for_save = directory + save_xyz_file(opted_geometry, element_list, original_input_file, i+1, directory)
161
+ next_input_file = save_xyz_file(opted_geometry, element_list, original_input_file, i+1, "")
162
+ args = original_args
163
+ if first_only_flag:
164
+ args.INPUT = init_input_file
165
+ else:
166
+ args.INPUT = next_input_file
167
+ print("Done.")
168
+
169
+ with open(ene_profile_filepath, "a") as f:
170
+ f.write(str(",".join(list(map(str, scan_job_list[i].tolist())))) + "," + str(energy) + "," + str(bias_energy) + "\n")
171
+ del bpa
172
+
173
+ print("Relaxed scan done ...")
174
+
175
+
176
+ def run_orientsearch():
177
+ """ Entry point for the orientation search script (orientation_search.py). """
178
+
179
+ def orientation_search(parser):
180
+ parser.add_argument("-nsample", "--number_of_samples", type=int, default=5, help='the number of sampling orientations')
181
+ parser.add_argument("-part", "--part", nargs="*", type=str, help='the part number (ex.) 1,2,3 or 1-3', default=None)
182
+ parser.add_argument("-dist", "--distance", type=float, default=5.0, help='the distance of parts [ang.]')
183
+ return parser
184
+
185
+
186
+ def num_parse(numbers):
187
+ sub_list = []
188
+
189
+ sub_tmp_list = numbers.split(",")
190
+ for sub in sub_tmp_list:
191
+ if "-" in sub:
192
+ for j in range(int(sub.split("-")[0]),int(sub.split("-")[1])+1):
193
+ sub_list.append(j)
194
+ else:
195
+ sub_list.append(int(sub))
196
+ return sub_list
197
+
198
+
199
+ def save_xyz_file(coord_list, element_list, file_name, n_sample):
200
+ no_ext_file_name = os.path.splitext(file_name)[0]
201
+ sample_file_name = no_ext_file_name+"_Sample_"+str(n_sample)+".xyz"
202
+ with open(sample_file_name, 'w') as f:
203
+ f.write(str(len(coord_list))+"\n")
204
+ f.write("Sample_"+str(n_sample)+"\n")
205
+ for i in range(len(coord_list)):
206
+ f.write(element_list[i]+" "+str(coord_list[i][0])+" "+str(coord_list[i][1])+" "+str(coord_list[i][2])+"\n")
207
+
208
+ return sample_file_name
209
+
210
+
211
+ def make_random_orientation_xyz_file(coord_list, part_list, part_dist, input_file_name, n_sample, element_list):#ang.
212
+ part_coord_list = []
213
+ part_element_list = []
214
+ for part in part_list:
215
+ tmp_part_coord_list = []
216
+ tmp_part_element_list = []
217
+ for p in part:
218
+ tmp_part_coord_list.append(coord_list[p-1])
219
+ tmp_part_element_list.append(element_list[p-1])
220
+ part_coord_list.append(np.array(tmp_part_coord_list))
221
+ part_element_list.append(tmp_part_element_list)
222
+
223
+ # Random rotation
224
+ rand_rotated_part_coord_list = []
225
+ for i in range(len(part_coord_list)):
226
+ part_center = calc_tools.Calculationtools().calc_center(part_coord_list[i], part_element_list[i])
227
+ centered_part_coord_list = part_coord_list[i] - part_center
228
+ random_x_angle = random.uniform(0, 2*np.pi)
229
+ random_y_angle = random.uniform(0, 2*np.pi)
230
+ random_z_angle = random.uniform(0, 2*np.pi)
231
+ print("Random angles (Radian): ", random_x_angle, random_y_angle, random_z_angle)
232
+ rotated_part_coord = calc_tools.rotate_molecule(centered_part_coord_list, "x", random_x_angle)
233
+ rotated_part_coord = calc_tools.rotate_molecule(rotated_part_coord, "y", random_y_angle)
234
+ rotated_part_coord = calc_tools.rotate_molecule(rotated_part_coord, "z", random_z_angle)
235
+ rand_rotated_part_coord_list.append(rotated_part_coord)
236
+
237
+
238
+ # Random translation
239
+ rand_tr_rot_part_coord_list = rand_rotated_part_coord_list
240
+
241
+ if len(rand_tr_rot_part_coord_list) == 2:
242
+ rand_dist = random.uniform(part_dist, 1.1*part_dist)
243
+ rand_tr_rot_part_coord_list[0] = rand_tr_rot_part_coord_list[0] - np.array([part_dist/2, 0, 0])
244
+ rand_tr_rot_part_coord_list[1] = rand_tr_rot_part_coord_list[1] + np.array([part_dist/2, 0, 0])
245
+ else:
246
+ fragm_num = len(rand_tr_rot_part_coord_list)
247
+ rand_dist = random.uniform(part_dist, 1.1*part_dist)
248
+ rand_sample_list = random.sample(range(fragm_num), fragm_num)
249
+ distance_polygen_point_from_center = 0.5 * rand_dist * np.tan((fragm_num - 2) * np.pi / fragm_num)
250
+ tmp_angle = 0.0
251
+ for i in range(len(rand_sample_list)):
252
+ rand_tr_rot_part_coord_list[rand_sample_list[i]] = rand_tr_rot_part_coord_list[rand_sample_list[i]] + np.array([distance_polygen_point_from_center * np.cos(tmp_angle), distance_polygen_point_from_center * np.sin(tmp_angle), 0])
253
+ tmp_angle += 2 * np.pi / fragm_num
254
+
255
+ combined_coord_list = np.zeros((len(coord_list), 3))
256
+
257
+ # Combine all parts
258
+ for i in range(len(part_list)):
259
+ part = part_list[i]
260
+
261
+ for j in range(len(part)):
262
+
263
+ combined_coord_list[part[j]-1] = rand_tr_rot_part_coord_list[i][j]
264
+
265
+ input_file_name = save_xyz_file(combined_coord_list, element_list, input_file_name, n_sample)
266
+
267
+ return input_file_name
268
+
269
+
270
+
271
+
272
+ parser = multioptpy.interface.init_parser()
273
+ parser = orientation_search(parser)
274
+ args = multioptpy.interface.optimizeparser(parser)
275
+
276
+ part_dist = args.distance
277
+
278
+ with open(args.INPUT, 'r') as f:
279
+ words = f.read().splitlines()
280
+
281
+ element_list = []
282
+ coord_list = []
283
+
284
+ for word in words:
285
+ splitted_word = word.split()
286
+
287
+ if len(splitted_word) > 3:
288
+ element_list.append(str(splitted_word[0]))
289
+ coord_list.append([float(splitted_word[1]), float(splitted_word[2]), float(splitted_word[3])])
290
+
291
+ coord_list = np.array(coord_list, dtype="float64")
292
+ atom_num = len(coord_list)
293
+
294
+ if args.part is None:
295
+ print("No part is specified. exit....")
296
+ sys.exit(0)
297
+
298
+ part_list = []
299
+ for i in range(len(args.part)):
300
+ part_list.append(num_parse(args.part[i]))
301
+
302
+ flattened_part_list = [item for sublist in part_list for item in sublist]
303
+ missing_parts = [num for num in range(1, atom_num+1) if num not in flattened_part_list]
304
+ if len(missing_parts) > 0:
305
+ part_list.append(missing_parts)
306
+
307
+ print("Number of Parts: ", len(part_list))
308
+ n_sample = args.number_of_samples
309
+
310
+ original_input_file = args.INPUT
311
+
312
+ for i in range(n_sample):
313
+ print("Sampling orientation: ", i)
314
+ args.INPUT = make_random_orientation_xyz_file(coord_list, part_list, part_dist, original_input_file, i, element_list)
315
+ bpa = multioptpy.optimization.Optimize(args)
316
+ bpa.run()
317
+
318
+
319
+ print("Sampling orientation: ", i, "done")
320
+
321
+ print("Orientation search done")
322
+
323
+
324
+ def run_autots():
325
+ """ Entry point for the Automated Transition State (AutoTS) workflow (run_autots.py). """
326
+ # ======================================================================
327
+ # AUTO-TS WORKFLOW (V1/V2) CONFIGURATION GUIDE
328
+ # ======================================================================
329
+ # Config is loaded from 'config.json'.
330
+ #
331
+ # V1 (Legacy): Uses top-level keys like 'step1_settings', 'skip_step1'.
332
+ # V2 (Dynamic): Uses the "workflow": [...] block to define execution.
333
+ #
334
+ # CRITICAL GUIDELINE:
335
+ # To understand options (e.g., 'opt_method', 'NSTEP'),
336
+ # refer to 'multioptpy/interface.py':
337
+ #
338
+ # 1. Step 1, 3, & 4 Settings: call_optimizeparser()
339
+ # 2. Step 2 Settings: call_nebparser()
340
+ # ======================================================================
341
+
342
+
343
+ def load_config_from_file(config_path):
344
+ """Loads configuration settings from a JSON file."""
345
+ try:
346
+ with open(config_path, 'r') as f:
347
+ config = json.load(f)
348
+ print(f"Successfully loaded configuration from {config_path}")
349
+ return config
350
+ except FileNotFoundError:
351
+ print(f"Error: Configuration file not found at {config_path}.")
352
+ sys.exit(1)
353
+ except json.JSONDecodeError:
354
+ print(f"Error: Failed to parse JSON file {config_path}. Check file format.")
355
+ sys.exit(1)
356
+ except Exception as e:
357
+ print(f"An unexpected error occurred loading config: {e}")
358
+ sys.exit(1)
359
+
360
+
361
+ def launch_workflow(config):
362
+ """
363
+ Launches the AutoTSWorkflow (v1 or v2) based on config.
364
+
365
+ Args:
366
+ config (dict): The complete configuration dictionary.
367
+ """
368
+
369
+ # --- 1. Apply settings (for v1 compatibility) ---
370
+ # This is still useful for v2 if 'stepX_settings' are used as base keys.
371
+ local_conf_name = os.path.basename(config.get("software_path_file_source", "software_path.conf"))
372
+ for i in range(1, 5): # Steps 1, 2, 3, and 4
373
+ step_key = f"step{i}_settings"
374
+ if step_key in config:
375
+ config[step_key]["software_path_file"] = local_conf_name
376
+ elif i == 4 and "step4_settings" not in config: # Create step4 settings if not in config
377
+ config["step4_settings"] = {"software_path_file": local_conf_name}
378
+
379
+ # --- 2. Print Summary ---
380
+ print("--- AutoTS Workflow Starting ---")
381
+ print(f"Input File: {config.get('initial_mol_file')}")
382
+
383
+ # --- 3. NEW: Dynamic v1/v2 Selection ---
384
+ if "workflow" in config:
385
+ print(">>> Detected 'workflow' key. Initializing AutoTSWorkflow_v2.")
386
+ workflow = AutoTSWorkflow_v2(config=config)
387
+ else:
388
+ print(">>> No 'workflow' key found. Initializing AutoTSWorkflow (v1).")
389
+ # Print v1-specific flags
390
+ print(f"Skip Step 1: {config.get('skip_step1', False)}")
391
+ print(f"Skip to Step 4: {config.get('skip_to_step4', False)}")
392
+ print(f"Run Step 4 (IRC): {config.get('run_step4', False)}")
393
+
394
+ if not config.get('skip_step1', False) and not config.get('skip_to_step4', False):
395
+ print(f"AFIR Params: {config.get('step1_settings', {}).get('manual_AFIR')}")
396
+
397
+ workflow = AutoTSWorkflow(config=config)
398
+
399
+ # --- 4. Run the selected workflow ---
400
+ # Both v1 and v2 classes have a .run_workflow() method
401
+ workflow.run_workflow()
402
+
403
+ def main():
404
+ """
405
+ Main function for command-line execution.
406
+ Parses CMD arguments, loads config, merges them, and calls launch_workflow.
407
+ (Modified for v1/v2 compatibility)
408
+ """
409
+ parser = argparse.ArgumentParser(
410
+ description="Run the Automated Transition State (AutoTS) workflow (v1 or v2)."
411
+ )
412
+
413
+ # --- (Parser arguments remain the same) ---
414
+ parser.add_argument(
415
+ "input_file",
416
+ type=str,
417
+ help="Path to the initial structure file. If --skip_to_step4 is used (v1), this must be the TS file."
418
+ )
419
+ parser.add_argument(
420
+ "-cfg", "--config_file",
421
+ type=str,
422
+ default="./config.json",
423
+ help="Path to the configuration JSON file. Default is './config.json'."
424
+ )
425
+ parser.add_argument(
426
+ "-ma", "--manual_AFIR",
427
+ nargs="*",
428
+ required=False,
429
+ help="Manual AFIR parameters for Step 1. Overrides config file's 'step1_settings'."
430
+ )
431
+ parser.add_argument(
432
+ "-osp", "--software_path_file",
433
+ type=str,
434
+ default="./software_path.conf",
435
+ help="Path to the 'software_path.conf' file. Defaults to './software_path.conf'"
436
+ )
437
+ parser.add_argument(
438
+ "-n", "--top_n",
439
+ type=int,
440
+ default=None, # Default will be read from JSON
441
+ help="Refine the top N highest energy candidates from NEB. Overrides config file."
442
+ )
443
+
444
+ # --- V1-specific flags ---
445
+ parser.add_argument(
446
+ "--skip_step1",
447
+ action="store_true",
448
+ help="Skip the AFIR scan (Step 1). The input_file must be the NEB trajectory file."
449
+ )
450
+ parser.add_argument(
451
+ "--run_step4",
452
+ action="store_true",
453
+ help="Run Step 4 (IRC + Endpoint Optimization) after Step 3 completes."
454
+ )
455
+ parser.add_argument(
456
+ "--skip_to_step4",
457
+ action="store_true",
458
+ help="Skip Steps 1-3 and run only Step 4. The 'input_file' must be the TS structure file."
459
+ )
460
+
461
+ args = parser.parse_args()
462
+
463
+ # --- 1. Load Base Configuration from File ---
464
+ workflow_config = load_config_from_file(args.config_file)
465
+
466
+ # --- 2. Override Config with CMD Arguments ---
467
+ # These apply to both v1 and v2
468
+ workflow_config["initial_mol_file"] = args.input_file
469
+ workflow_config["software_path_file_source"] = os.path.abspath(args.software_path_file)
470
+
471
+ # Merge V1-specific CMD flags (v2 will ignore them)
472
+ workflow_config["skip_step1"] = args.skip_step1
473
+ workflow_config["run_step4"] = args.run_step4
474
+ workflow_config["skip_to_step4"] = args.skip_to_step4
475
+
476
+ if args.top_n is not None:
477
+ workflow_config["top_n_candidates"] = args.top_n
478
+
479
+ # --- 3. AFIR Validation (v1/v2 compatibility logic) ---
480
+
481
+ # Ensure 'step1_settings' key exists for v1 compatibility
482
+ workflow_config.setdefault("step1_settings", {})
483
+
484
+ # Check if v1 is running step 1
485
+ is_v1_running_step1 = (
486
+ "workflow" not in workflow_config and
487
+ not args.skip_step1 and
488
+ not args.skip_to_step4
489
+ )
490
+
491
+ # Check if v2 is running step 1
492
+ is_v2_running_step1 = False
493
+ if "workflow" in workflow_config:
494
+ for entry in workflow_config.get("workflow", []):
495
+ if entry.get("step") == "step1" and entry.get("enabled", True):
496
+ is_v2_running_step1 = True
497
+ break
498
+
499
+ # Check AFIR status in config (base 'step1_settings' only) and CMD
500
+ config_has_afir = workflow_config["step1_settings"].get("manual_AFIR")
501
+ cmd_has_afir = args.manual_AFIR is not None
502
+
503
+ if cmd_has_afir:
504
+ # Case 1: CMD argument is given. It *always* overrides the base 'step1_settings'.
505
+ workflow_config["step1_settings"]["manual_AFIR"] = args.manual_AFIR
506
+ print(f"Using 'manual_AFIR' from command line (overrides 'step1_settings'): {args.manual_AFIR}")
507
+ # This will be used by v1, or by v2 if 'step1_settings' is its base key.
508
+
509
+ elif not config_has_afir:
510
+ # Case 2: No AFIR in CMD, and *also* no AFIR in the base 'step1_settings'.
511
+
512
+ if is_v1_running_step1:
513
+ # For v1, this is a fatal error.
514
+ print("\nError (v1 mode): 'manual_AFIR' is not defined in 'step1_settings' and was not provided via -ma.")
515
+ print(" Please add 'manual_AFIR' to your JSON or use the -ma argument.")
516
+ sys.exit(1)
517
+
518
+ elif is_v2_running_step1:
519
+ # For v2, this is a warning, as it might be defined in 'param_override'.
520
+ print(f"Warning: 'manual_AFIR' not found in 'step1_settings' or via -ma.")
521
+ print(" (v2 mode): Ensure 'manual_AFIR' is defined in your 'workflow' entry")
522
+ print(" (either in the base 'settings_key' block or 'param_override').")
523
+
524
+ elif is_v1_running_step1 and config_has_afir:
525
+ # Case 3: v1 is running, no CMD override, but config has it. Just print confirmation.
526
+ print(f"Using 'manual_AFIR' from config file: {config_has_afir}")
527
+
528
+ # --- 4. Call the launcher function ---
529
+ launch_workflow(workflow_config)
530
+
531
+
532
+ main()
533
+
534
+
535
+ def run_confsearch():
536
+ """ Entry point for the conformation search script (conformation_search.py). """
537
+
538
+ bohr2ang = 0.529177210903
539
+
540
+ #Example: python conformation_search.py s8_for_confomation_search_test.xyz -xtb GFN2-xTB -ns 2000
541
+
542
+ def calc_boltzmann_distribution(energy_list, temperature=298.15):
543
+ """
544
+ Calculate the Boltzmann distribution.
545
+ """
546
+ energy_list = np.array(energy_list)
547
+ energy_list = energy_list - min(energy_list)
548
+ energy_list = energy_list * 627.509
549
+ boltzmann_distribution = np.exp(-energy_list / (0.0019872041 * temperature))
550
+ boltzmann_distribution = boltzmann_distribution / np.sum(boltzmann_distribution)
551
+
552
+ return boltzmann_distribution
553
+
554
+ def get_index_from_distribution(probabilities):
555
+ if not abs(sum(probabilities) - 1.0) < 1e-8:
556
+ raise ValueError("the sum of probabilities is not 1.0")
557
+
558
+ cumulative_distribution = []
559
+ cumulative_sum = 0
560
+ for p in probabilities:
561
+ cumulative_sum += p
562
+ cumulative_distribution.append(cumulative_sum)
563
+
564
+ rand = random.random()
565
+
566
+ for i, threshold in enumerate(cumulative_distribution):
567
+ if rand < threshold:
568
+ return i
569
+
570
+
571
+
572
+ def calc_distance_matrix(geom_num_list):
573
+ natoms = len(geom_num_list)
574
+ combination_natoms = int(natoms * (natoms - 1) / 2)
575
+ distance_matrix = np.zeros((combination_natoms))
576
+
577
+ count = 0
578
+ for i, j in itertools.combinations(range(natoms), 2):
579
+ distance_matrix[count] = np.linalg.norm(geom_num_list[i] - geom_num_list[j])
580
+ count += 1
581
+
582
+ return distance_matrix
583
+
584
+ def sort_distance_matrix(distance_matrix):
585
+ sort_distance_matrix = np.sort(distance_matrix)
586
+ return sort_distance_matrix
587
+
588
+ def check_identical(geom_num_list_1, geom_num_list_2, threshold=1e-3):
589
+ distance_matrix_1 = calc_distance_matrix(geom_num_list_1)
590
+ distance_matrix_2 = calc_distance_matrix(geom_num_list_2)
591
+ sort_distance_matrix_1 = sort_distance_matrix(distance_matrix_1)
592
+ sort_distance_matrix_2 = sort_distance_matrix(distance_matrix_2)
593
+
594
+ # Check if the two geometries are identical
595
+ if np.all(np.abs(sort_distance_matrix_1 - sort_distance_matrix_2) < threshold):
596
+ print("The two geometries are identical.")
597
+ return True
598
+ else:
599
+ print("The two geometries are not identical.")
600
+ return False
601
+
602
+ def read_xyz(file_name):
603
+ with open(file_name, 'r') as f:
604
+ data = f.read().splitlines()
605
+
606
+ geom_num_list = []
607
+ element_list = []
608
+
609
+ for i in range(2, len(data)):
610
+ splitted_data = data[i].split()
611
+ element_list.append(splitted_data[0])
612
+ geom_num_list.append(splitted_data[1:4])
613
+
614
+ geom_num_list = np.array(geom_num_list, dtype="float64")
615
+
616
+ return geom_num_list, element_list
617
+
618
+ def conformation_search(parser):
619
+ parser.add_argument("-bf", "--base_force", type=float, default=100.0, help='bias force to search conformations (default: 100.0 kJ)')
620
+ parser.add_argument("-ms", "--max_samples", type=int, default=50, help='the number of trial of calculation (default: 50)')
621
+ parser.add_argument("-nl", "--number_of_lowest", type=int, default=5, help='termination condition of calculation for updating list (default: 5)')
622
+ parser.add_argument("-nr", "--number_of_rank", type=int, default=10, help='termination condition of calculation for making list (default: 10)')
623
+ parser.add_argument("-tgta", "--target_atoms", nargs="*", type=str, help='the atom to add bias force to perform conformational search (ex.) 1,2,3 or 1-3', default=None)
624
+ parser.add_argument("-st", "--sampling_temperature", type=float, help='set temperature to select conformer using Boltzmann distribution (default) 298.15 (K)', default=298.15)
625
+ parser.add_argument("-nost", "--no_stochastic", action="store_true", help='no switching EQ structure during conformation sampling')
626
+ return parser
627
+
628
+ def return_pair_idx(i, j):
629
+ ii = max(i, j) + 1
630
+ jj = min(i, j) + 1
631
+ pair_idx = int(ii * (ii - 1) / 2 - (ii - jj)) - 1
632
+ return pair_idx
633
+
634
+ def num_parse(numbers):
635
+ sub_list = []
636
+
637
+ sub_tmp_list = numbers.split(",")
638
+ for sub in sub_tmp_list:
639
+ if "-" in sub:
640
+ for j in range(int(sub.split("-")[0]),int(sub.split("-")[1])+1):
641
+ sub_list.append(j)
642
+ else:
643
+ sub_list.append(int(sub))
644
+ return sub_list
645
+
646
+
647
+ def save_xyz_file(coord_list, element_list, file_name, add_name):
648
+ no_ext_file_name = os.path.splitext(file_name)[0]
649
+ sample_file_name = no_ext_file_name+"_"+str(add_name)+".xyz"
650
+ with open(sample_file_name, 'w') as f:
651
+ f.write(str(len(coord_list))+"\n")
652
+ f.write("Sample_"+str(add_name)+"\n")
653
+ for i in range(len(coord_list)):
654
+ f.write(element_list[i]+" "+str(coord_list[i][0])+" "+str(coord_list[i][1])+" "+str(coord_list[i][2])+"\n")
655
+
656
+ return sample_file_name
657
+
658
+
659
+ def read_energy_file(file_name):
660
+ """
661
+ Read energy file and return energy list.
662
+ Format:
663
+ -3.000000
664
+ -1.000000
665
+ -2.000000
666
+ ....
667
+ """
668
+ with open(file_name, 'r') as f:
669
+ data = f.read().splitlines()
670
+
671
+ energy_list = []
672
+
673
+ for i in range(len(data)):
674
+ splitted_data = data[i].split()
675
+ if len(splitted_data) == 0:
676
+ continue
677
+ energy_list.append(float(splitted_data[0]))
678
+
679
+ return energy_list
680
+
681
+ def make_tgt_atom_pair(geom_num_list, element_list, target_atoms):
682
+ norm_dist_min = 1.0
683
+ norm_dist_max = 8.0
684
+ norm_distance_list = calc_tools.calc_normalized_distance_list(geom_num_list, element_list)
685
+ bool_tgt_atom_list = np.where((norm_dist_min < norm_distance_list) & (norm_distance_list < norm_dist_max), True, False)
686
+ updated_target_atom_pairs = []
687
+ for i, j in itertools.combinations(target_atoms, 2):
688
+
689
+ pair_idx = return_pair_idx(i, j)
690
+ if bool_tgt_atom_list[pair_idx]:
691
+ updated_target_atom_pairs.append([[i, j], "p"])
692
+ updated_target_atom_pairs.append([[i, j], "m"])
693
+
694
+ return updated_target_atom_pairs
695
+
696
+
697
+ def is_identical(conformer, energy, energy_list, folder_name, init_INPUT, ene_threshold=1e-4, dist_threshold=1e-1):
698
+ no_ext_init_INPUT = os.path.splitext(init_INPUT)[0]
699
+ ene_identical_list = []
700
+
701
+ for i in range(len(energy_list)):
702
+ if abs(energy_list[i] - energy) < ene_threshold:
703
+ print("Energy is identical. Check distance matrix.")
704
+ ene_identical_list.append(i)
705
+
706
+ if len(ene_identical_list) == 0:
707
+ print("Energy is not identical. Register this conformer.")
708
+ return False
709
+
710
+ for i in range(len(energy_list)):
711
+ conformer_file_name = folder_name+"/"+no_ext_init_INPUT+"_EQ"+str(i)+".xyz"
712
+ conformer_geom_num_list, conformer_element_list = read_xyz(conformer_file_name)
713
+
714
+ bool_identical = check_identical(conformer, conformer_geom_num_list, threshold=dist_threshold)
715
+
716
+ if bool_identical:
717
+ print("This conformer is identical to the existing conformer. Skip this conformer.")
718
+ return True
719
+
720
+
721
+ print("This conformer is not identical to the existing conformer. Register this conformer.")
722
+ return False
723
+
724
+ def switch_conformer(energy_list, temperature=298.15):
725
+ boltzmann_distribution = calc_boltzmann_distribution(energy_list, temperature)
726
+ idx = get_index_from_distribution(boltzmann_distribution)
727
+ return idx
728
+
729
+
730
+ parser = multioptpy.interface.init_parser()
731
+ parser = conformation_search(parser)
732
+
733
+ args = multioptpy.interface.optimizeparser(parser)
734
+ no_stochastic = args.no_stochastic
735
+ init_geom_num_list, init_element_list = read_xyz(args.INPUT)
736
+ sampling_temperature = args.sampling_temperature
737
+ folder_name = os.path.splitext(args.INPUT)[0]+"_"+str(int(args.base_force))+"KJ_CS_REPORT"
738
+
739
+ if not os.path.exists(folder_name):
740
+ os.makedirs(folder_name)
741
+
742
+
743
+ energy_list_file_path = folder_name+"/EQ_energy.dat"
744
+ if os.path.exists(energy_list_file_path):
745
+ energy_list = read_energy_file(energy_list_file_path)
746
+
747
+ else:
748
+ energy_list = []
749
+
750
+ with open(folder_name+"/input.txt", "a") as f:
751
+ f.write(str(vars(args))+"\n")
752
+
753
+
754
+ if args.target_atoms is not None:
755
+ target_atoms = [i-1 for i in num_parse(args.target_atoms[0])]
756
+ else:
757
+ target_atoms = [i for i in range(len(init_geom_num_list))]
758
+
759
+ init_INPUT = args.INPUT
760
+ init_AFIR_CONFIG = args.manual_AFIR
761
+
762
+ atom_pair_list = make_tgt_atom_pair(init_geom_num_list, init_element_list, target_atoms)
763
+ random.shuffle(atom_pair_list)
764
+
765
+ # prepare for the first calculation
766
+ prev_rank_list = None
767
+ no_update_count = 0
768
+ if len(energy_list) == 0:
769
+ count = len(energy_list)
770
+ else:
771
+ count = len(energy_list) - 1
772
+ reason = ""
773
+ if len(energy_list) == 0:
774
+ print("initial conformer.")
775
+ bpa = multioptpy.optimization.Optimize(args)
776
+ bpa.run()
777
+ if not bpa.optimized_flag:
778
+ print("Optimization is failed. Exit...")
779
+ exit()
780
+ energy = bpa.final_energy
781
+ init_conformer = bpa.final_geometry #Bohr
782
+ init_conformer = init_conformer * bohr2ang #Angstrom
783
+ energy_list.append(energy)
784
+ with open(energy_list_file_path, 'a') as f:
785
+ f.write(str(energy)+"\n")
786
+ print("initial conformer.")
787
+ print("Energy: ", energy)
788
+ save_xyz_file(init_conformer, init_element_list, folder_name+"/"+init_INPUT, "EQ"+str(0))
789
+
790
+ if len(atom_pair_list) == 0:
791
+ print("Cannot make atom_pair list. exit...")
792
+ exit()
793
+ else:
794
+ with open(folder_name+"/search_atom_pairs.log", 'a') as f:
795
+ for atom_pair in atom_pair_list:
796
+ f.write(str(atom_pair[0])+" "+str(atom_pair[1])+"\n")
797
+
798
+ for i in range(args.max_samples):
799
+ if os.path.exists(folder_name+"/end.txt"):
800
+ print("The stop signal is detected. Exit....")
801
+ reason = "The stop signal is detected. Exit...."
802
+ break
803
+
804
+ if len(atom_pair_list) <= i + 1:
805
+ print("All possible atom pairs are searched. Exit....")
806
+ reason = "All possible atom pairs are searched. Exit...."
807
+ break
808
+
809
+ print("Sampling conformation: ", i)
810
+
811
+
812
+ atom_pair = atom_pair_list[i][0]
813
+ if atom_pair_list[i][1] == "p":
814
+ args.manual_AFIR = init_AFIR_CONFIG + [str(args.base_force), str(atom_pair[0]+1), str(atom_pair[1]+1)]
815
+ else:
816
+ args.manual_AFIR = init_AFIR_CONFIG + [str(-args.base_force), str(atom_pair[0]+1), str(atom_pair[1]+1)]
817
+
818
+ bpa = multioptpy.optimization.Optimize(args)
819
+ bpa.run()
820
+ DC_check_flag = bpa.state.DC_check_flag
821
+ if not DC_check_flag:
822
+ bias_opted_geom_num_list = bpa.final_geometry #Bohr
823
+ bias_opted_geom_num_list = bias_opted_geom_num_list * bohr2ang #Angstrom
824
+ sample_file_name = save_xyz_file(bias_opted_geom_num_list, init_element_list, init_INPUT, "tmp")
825
+ args.INPUT = sample_file_name
826
+ args.manual_AFIR = init_AFIR_CONFIG
827
+ bpa = multioptpy.optimization.Optimize(args)
828
+ bpa.run()
829
+ optimized_flag = bpa.optimized_flag
830
+ energy = bpa.final_energy
831
+ conformer = bpa.final_geometry #Bohr
832
+ conformer = conformer * bohr2ang #Angstrom
833
+ # Check identical
834
+ bool_identical = is_identical(conformer, energy, energy_list, folder_name, init_INPUT)
835
+ else:
836
+ optimized_flag = False
837
+ bool_identical = True
838
+
839
+
840
+ if bool_identical or not optimized_flag or DC_check_flag:
841
+ if not optimized_flag:
842
+ print("Optimization is failed...")
843
+ if DC_check_flag:
844
+ print("DC is detected...")
845
+
846
+ else:
847
+ count += 1
848
+ energy_list.append(energy)
849
+
850
+ with open(energy_list_file_path, 'w') as f:
851
+ for energy in energy_list:
852
+ f.write(str(energy)+"\n")
853
+
854
+ print("Find new conformer.")
855
+ print("Energy: ", energy)
856
+ save_xyz_file(conformer, init_element_list, folder_name+"/"+init_INPUT, "EQ"+str(count))
857
+
858
+
859
+ # Check termination criteria
860
+ if len(energy_list) > args.number_of_rank:
861
+ sorted_energy_list = np.sort(np.array(energy_list))
862
+ rank_list = sorted_energy_list[:args.number_of_rank]
863
+
864
+ if np.all(rank_list == prev_rank_list):
865
+ no_update_count += 1
866
+ else:
867
+ no_update_count = 0
868
+
869
+ prev_rank_list = rank_list
870
+
871
+ if no_update_count > args.number_of_lowest:
872
+ print("The number of lowest energy conformers is not updated. Exit....")
873
+ reason = "The number of lowest energy conformers is not updated. Exit...."
874
+ break
875
+ with open(folder_name+"/no_update_count.log", "a") as f:
876
+ f.write(str(i)+" "+str(no_update_count)+"\n")
877
+
878
+ else:
879
+ print("The number of conformers is less than the number of rank.")
880
+
881
+ # Switch conformer
882
+ if len(energy_list) > 1:
883
+ if no_stochastic:
884
+ idx = 0
885
+ else:
886
+ if i % 5 == 0:
887
+ idx = switch_conformer(energy_list, sampling_temperature*10)
888
+ else:
889
+ idx = switch_conformer(energy_list, sampling_temperature)
890
+
891
+ no_ext_init_INPUT = os.path.splitext(init_INPUT)[0]
892
+ args.INPUT = folder_name + "/" + no_ext_init_INPUT + "_EQ" + str(idx) + ".xyz"
893
+ print("Switch conformer: EQ"+str(idx))
894
+ with open(folder_name+"/switch_conformer.log", 'a') as f:
895
+ f.write("Trial "+str(i)+": Switch conformer: EQ"+str(idx)+"\n")
896
+ else:
897
+ args.INPUT = init_INPUT
898
+
899
+ ##########
900
+ else:
901
+ print("Max samples are reached. Exit....")
902
+ reason = "Max samples are reached. Exit...."
903
+ energy_list_suumary_file_path = folder_name+"/EQ_summary.log"
904
+ with open(energy_list_suumary_file_path, "w") as f:
905
+ f.write("Summary\n"+"Reason of Termination: "+reason+"\n")
906
+ print("conformer of lowest energy: ", min(energy_list))
907
+ f.write("conformer of lowest energy: "+str(min(energy_list))+"\n")
908
+ print("structure of lowest energy: ", "EQ"+str(energy_list.index(min(energy_list))))
909
+ f.write("structure of lowest energy: "+"EQ"+str(energy_list.index(min(energy_list)))+"\n")
910
+ print("conformer of highest energy: ", max(energy_list))
911
+ f.write("conformer of highest energy: "+str(max(energy_list))+"\n")
912
+ print("structure of highest energy: ", "EQ"+str(energy_list.index(max(energy_list))))
913
+ f.write("structure of highest energy: "+"EQ"+str(energy_list.index(max(energy_list)))+"\n")
914
+
915
+
916
+ print("Conformation search is finished.")