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,834 @@
1
+ import numpy as np
2
+ import copy
3
+ import torch
4
+
5
+ from multioptpy.Parameters.parameter import atomic_mass, UnitValueLib
6
+ from multioptpy.Utils.calc_tools import (calc_bond_length_from_vec,
7
+ calc_angle_from_vec,
8
+ calc_dihedral_angle_from_vec, change_atom_distance_both_side,
9
+ change_bond_angle_both_side,
10
+ change_torsion_angle_both_side,
11
+ change_fragm_distance_both_side,
12
+ Calculationtools
13
+ )
14
+ from multioptpy.Coordinate.redundant_coordinate import (TorchDerivatives,
15
+ partial_stretch_B_matirx,
16
+ partial_bend_B_matrix,
17
+ partial_torsion_B_matrix,
18
+ RedundantInternalCoordinates,
19
+ torch_B_matrix,
20
+ torch_B_matrix_derivative,
21
+ torch_calc_distance,
22
+ torch_calc_fragm_distance,
23
+ torch_calc_angle,
24
+ torch_calc_dihedral_angle,
25
+ calc_dot_B_deriv_int_grad,
26
+ calc_int_hess_from_pBmat_for_non_stationary_point,
27
+ calc_cart_hess_from_pBmat_for_non_stationary_point,
28
+ calc_int_cart_coupling_hess_from_pBmat_for_non_stationary_point,
29
+ calc_int_grad_from_pBmat,
30
+ calc_cart_grad_from_pBmat,
31
+ )
32
+
33
+ def isduplicated(num_list):
34
+ numbers = [item for sublist in num_list for item in sublist]
35
+ boolean = len(numbers) != len(set(numbers))
36
+ return boolean
37
+
38
+
39
+
40
+ def shake_parser(constraints):
41
+ bond_list = []
42
+ angle_list = []
43
+ dihedral_angle_list = []
44
+
45
+ for i in range(len(constraints)):
46
+ constraint = constraints[i].split(",")
47
+ if len(constraint) == 3:
48
+ bond_list.append([float(constraint[0])]+list(map(int, constraint[1:])))
49
+ elif len(constraint) == 4:
50
+ angle_list.append([float(constraint[0])]+list(map(int, constraint[1:])))
51
+ elif len(constraint) == 5:
52
+ dihedral_angle_list.append([float(constraint[0])]+list(map(int, constraint[1:])))
53
+ else:
54
+ print("error")
55
+ raise "error (invaild input of constraint conditions)"
56
+ constraints_list = [bond_list, angle_list, dihedral_angle_list]
57
+ return constraints_list
58
+
59
+
60
+
61
+ class SHAKE:
62
+ def __init__(self, time_scale, constraints=[]):
63
+ #ref.: Journal of Computational Physics. 23, (3), 327–341.
64
+ self.convergent_criterion = 1e-5
65
+ self.maxiter = 100000
66
+ self.time_scale = time_scale
67
+ self.constraint_condition = constraints[0] + constraints[1] + constraints[2]
68
+
69
+
70
+ def run(self, geom_num_list, prev_geom_num_list, momentum_list, element_list):
71
+ print("applying constraint conditions...")
72
+ new_geometry = copy.copy(geom_num_list)
73
+ new_momentum_list = copy.copy(momentum_list)
74
+ for iter in range(self.maxiter):
75
+ isconverged = True
76
+ for constraint in self.constraint_condition:
77
+ if len(constraint) == 3: # bond
78
+ idx_i = constraint[1] - 1
79
+ idx_j = constraint[2] - 1
80
+ constraint_distance = constraint[0] / UnitValueLib().bohr2angstroms
81
+ r_ij = new_geometry[idx_i] - new_geometry[idx_j]
82
+ check_convergence = abs(constraint_distance - np.linalg.norm(r_ij))
83
+
84
+ if check_convergence < self.convergent_criterion:
85
+
86
+ continue
87
+ isconverged = False
88
+ prev_r_ij = prev_geom_num_list[idx_i] - prev_geom_num_list[idx_j]
89
+ g_ij = (np.linalg.norm(r_ij) ** 2 - constraint_distance ** 2) / (2 * (np.sum(r_ij * prev_r_ij)) * (1/atomic_mass(element_list[idx_i]) + 1/atomic_mass(element_list[idx_j])))
90
+ new_geometry[idx_i] -= g_ij / atomic_mass(element_list[idx_i]) * prev_r_ij
91
+ new_geometry[idx_j] += g_ij / atomic_mass(element_list[idx_j]) * prev_r_ij
92
+ new_momentum_list[idx_i] -= g_ij / self.time_scale * prev_r_ij
93
+ new_momentum_list[idx_j] += g_ij / self.time_scale * prev_r_ij
94
+
95
+ elif len(constraint) == 4: # angle
96
+ # ref.:J. Chem. Phys. 133, 034114 (2010)
97
+ idx_i = constraint[1] - 1
98
+ idx_j = constraint[2] - 1
99
+ idx_k = constraint[3] - 1
100
+ constraint_angle = np.deg2rad(constraint[0])
101
+ r_ij = new_geometry[idx_i] - new_geometry[idx_j]
102
+ r_kj = new_geometry[idx_k] - new_geometry[idx_j]
103
+ inner_product_r_ij_r_kj = np.sum(r_ij * r_kj)
104
+ cos = inner_product_r_ij_r_kj / (np.linalg.norm(r_ij) * np.linalg.norm(r_kj))
105
+ constraint_cos = np.cos(constraint_angle)
106
+ check_convergence = abs(cos ** 2 - constraint_cos ** 2)
107
+ #print(check_convergence)
108
+ if check_convergence < self.convergent_criterion:
109
+
110
+ continue
111
+ isconverged = False
112
+ h_i = -2 * cos * (-1 * cos * r_ij / np.linalg.norm(r_ij) + r_kj / np.linalg.norm(r_kj)) / np.linalg.norm(r_ij) * (self.time_scale ** 2 / atomic_mass(element_list[idx_i]))
113
+ h_k = -2 * cos * (-1 * cos * r_kj / np.linalg.norm(r_kj) + r_ij / np.linalg.norm(r_ij)) / np.linalg.norm(r_kj) * (self.time_scale ** 2 / atomic_mass(element_list[idx_k]))
114
+ h_j = -1 * (h_i + h_k)
115
+ LAMBDA = 2 * cos * (((np.sum(-1 * r_ij * (h_j - h_k)) + np.sum(-1 * r_kj * (h_j - h_i))) / (np.linalg.norm(r_ij) * np.linalg.norm(r_kj))) -1 * ((np.sum(-1 * r_ij * (h_j - h_i)) / np.linalg.norm(r_ij) ** 2) + (np.sum(-1 * r_kj * (h_j - h_k)) / np.linalg.norm(r_kj) ** 2)) * cos)
116
+
117
+ new_momentum_list[idx_i] = h_i * self.time_scale
118
+ new_momentum_list[idx_j] = h_j * self.time_scale
119
+ new_momentum_list[idx_k] = h_k * self.time_scale
120
+
121
+ new_geometry[idx_i] -= 1e+5 * LAMBDA * h_i
122
+ new_geometry[idx_j] -= 1e+5 * LAMBDA * h_j
123
+ new_geometry[idx_k] -= 1e+5 * LAMBDA * h_k
124
+
125
+
126
+ else: # dihedral angle
127
+ # ref.:J. Chem. Phys. 133, 034114 (2010)
128
+ idx_a = constraint[1] - 1
129
+ idx_b = constraint[2] - 1
130
+ idx_c = constraint[3] - 1
131
+ idx_d = constraint[4] - 1
132
+ constraint_dihedral_angle = np.deg2rad(constraint[0])
133
+ r_ba = new_geometry[idx_b] - new_geometry[idx_a]
134
+ r_bc = new_geometry[idx_b] - new_geometry[idx_c]
135
+ r_cd = new_geometry[idx_c] - new_geometry[idx_d]
136
+ a = r_ba -1 * (np.sum(r_ba * r_bc / np.linalg.norm(r_bc)) * r_bc / np.linalg.norm(r_bc))
137
+ b = r_cd -1 * (np.sum(r_cd * r_bc / np.linalg.norm(r_bc)) * r_bc / np.linalg.norm(r_bc))
138
+ cos = np.sum(a / np.linalg.norm(a) * b / np.linalg.norm(b))
139
+ constraint_cos = np.cos(constraint_dihedral_angle)
140
+ check_convergence = abs(cos ** 2 - constraint_cos ** 2)
141
+
142
+ if check_convergence < self.convergent_criterion:
143
+ continue
144
+ isconverged = False
145
+ h_a = 2 * cos * (1 / (np.linalg.norm(a))) * (b / np.linalg.norm(b) -1 * cos * a / np.linalg.norm(a)) * (self.time_scale ** 2 / atomic_mass(element_list[idx_a]))
146
+ h_d = 2 * cos * (1 / (np.linalg.norm(b))) * (a / np.linalg.norm(a) -1 * cos * b / np.linalg.norm(b)) * (self.time_scale ** 2 / atomic_mass(element_list[idx_d]))
147
+ h_b = 2 * cos * (h_a / (2 * cos) * ((np.sum(r_ba * r_bc / np.linalg.norm(r_bc)) / np.linalg.norm(r_bc)) -1) + h_d / (2 * cos) * (np.sum(r_cd * r_bc / np.linalg.norm(r_bc)) / np.linalg.norm(r_bc))) * (self.time_scale ** 2 / atomic_mass(element_list[idx_b]))
148
+ h_c = 2 * cos * (-1 * h_d / (2 * cos) * ((np.sum(r_cd * r_bc / np.linalg.norm(r_bc)) / np.linalg.norm(r_bc)) -1) -1* h_a / (2 * cos) * (np.sum(r_ba * r_bc / np.linalg.norm(r_bc)) / np.linalg.norm(r_bc))) * (self.time_scale ** 2 / atomic_mass(element_list[idx_c]))
149
+ cross_r_ab_r_bc = np.cross(-1*r_ba, r_bc)
150
+ cross_r_cd_h_bc = np.cross(r_cd, (h_b - h_c))
151
+ cross_h_cd_r_bc = np.cross((h_c - h_d), r_bc)
152
+ cross_r_bc_r_cd = np.cross(r_bc, r_cd)
153
+ cross_r_bc_h_ab = np.cross(r_bc, (h_a - h_b))
154
+ cross_h_bc_r_ab = np.cross((h_b - h_c), -1*r_ba)
155
+
156
+
157
+ LAMBDA = -2 * cos * (((np.sum(cross_r_ab_r_bc * (cross_r_cd_h_bc + cross_h_cd_r_bc)) + np.sum(cross_r_bc_r_cd * (cross_r_bc_h_ab + cross_h_bc_r_ab))) / (np.linalg.norm(cross_r_ab_r_bc) * np.linalg.norm(cross_r_bc_r_cd))) -1 * ((np.sum(cross_r_ab_r_bc * (cross_r_bc_h_ab + cross_h_bc_r_ab))/np.linalg.norm(cross_r_ab_r_bc) ** 2) + (np.sum(cross_r_bc_r_cd * (cross_r_cd_h_bc + cross_h_cd_r_bc))/np.linalg.norm(cross_r_bc_r_cd) ** 2)) * cos)
158
+ new_momentum_list[idx_a] = h_a * self.time_scale
159
+ new_momentum_list[idx_b] = h_b * self.time_scale
160
+ new_momentum_list[idx_c] = h_c * self.time_scale
161
+ new_momentum_list[idx_d] = h_d * self.time_scale
162
+
163
+ new_geometry[idx_a] -= 1e+7 * LAMBDA * h_a
164
+ new_geometry[idx_b] -= 1e+7 * LAMBDA * h_b
165
+ new_geometry[idx_c] -= 1e+7 * LAMBDA * h_c
166
+ new_geometry[idx_d] -= 1e+7 * LAMBDA * h_d
167
+
168
+ if isconverged:
169
+ print("converged!!! (SHAKE)")
170
+ break
171
+ else:
172
+ print("not converged... (SHAKE)")
173
+
174
+ return new_geometry, new_momentum_list
175
+
176
+ class GradientSHAKE:
177
+ def __init__(self, constraints=[]):
178
+ #ref.:J Comput Chem 1995, 16 (11), 1351–1356.
179
+ self.convergent_criterion = 1e-5
180
+ self.maxiter = 100000
181
+ self.constraint_condition = constraints[0] + constraints[1] + constraints[2]
182
+
183
+ def run_grad(self, prev_geom_num_list, gradient_list):
184
+ new_gradient = gradient_list
185
+ #Gradient SHAKE
186
+ for iter in range(self.maxiter):
187
+ isconverged = True
188
+ for constraint in self.constraint_condition:
189
+ if len(constraint) == 3: # bond
190
+ idx_i = constraint[1] - 1
191
+ idx_j = constraint[2] - 1
192
+ constraint_distance = constraint[0] / UnitValueLib().bohr2angstroms
193
+ prev_r_ij = prev_geom_num_list[idx_i] - prev_geom_num_list[idx_j]
194
+ relative_force = gradient_list[idx_i] - gradient_list[idx_j]
195
+ delta = np.sum(relative_force * prev_r_ij)
196
+ delta_2 = abs(delta * 0.01 / constraint_distance)
197
+
198
+ if delta_2 < self.convergent_criterion:
199
+ continue
200
+ isconverged = False
201
+ eta_ij = delta / (2 * constraint_distance ** 2)
202
+
203
+ new_gradient[idx_i] -= eta_ij * prev_r_ij
204
+ new_gradient[idx_j] += eta_ij * prev_r_ij
205
+
206
+
207
+ elif len(constraint) == 4: # angle
208
+ print("Gradient SHAKE for angle is not implemented...")
209
+ """
210
+ idx_i = constraint[1] - 1
211
+ idx_j = constraint[2] - 1
212
+ idx_k = constraint[3] - 1
213
+ constraint_angle = np.deg2rad(constraint[0])
214
+ prev_r_ij = prev_geom_num_list[idx_i] - prev_geom_num_list[idx_j]
215
+ prev_r_kj = prev_geom_num_list[idx_k] - prev_geom_num_list[idx_j]
216
+ relative_force_ij = gradient_list[idx_i] - gradient_list[idx_j]
217
+ relative_force_kj = gradient_list[idx_k] - gradient_list[idx_j]
218
+ inner_product_r_ij_r_kj = np.sum(prev_r_ij * prev_r_kj)
219
+ cos = inner_product_r_ij_r_kj / (np.linalg.norm(prev_r_ij) * np.linalg.norm(prev_r_kj))
220
+ constraint_cos = np.cos(constraint_angle)
221
+
222
+ delta_ij = np.sum(relative_force_ij * prev_r_ij)
223
+ delta_kj = np.sum(relative_force_kj * prev_r_kj)
224
+ delta_2 = abs((delta_ij + delta_kj) * 1e-18/ constraint_cos)
225
+ #print(delta_2)
226
+ if delta_2 < self.convergent_criterion:
227
+ continue
228
+ isconverged = False
229
+ eta_ij = delta_ij / (2 * constraint_cos ** 2 + 1e+8)
230
+ eta_kj = delta_kj / (2 * constraint_cos ** 2 + 1e+8)
231
+ new_gradient[idx_i] -= eta_ij * prev_r_ij
232
+ new_gradient[idx_k] -= eta_kj * prev_r_kj
233
+ new_gradient[idx_j] += eta_ij * prev_r_ij + eta_kj * prev_r_kj
234
+ """
235
+ else: # dihedral angle
236
+ print("Gradient SHAKE for dihedral angle is not implemented...")
237
+
238
+ if isconverged:
239
+ print("converged!!! (Gradient SHAKE)")
240
+ break
241
+ else:
242
+ print("not converged... (Gradient SHAKE)")
243
+ return new_gradient
244
+
245
+ def run_coord(self, prev_geom_num_list, geom_num_list, element_list):
246
+ #SHAKE for energy minimalization
247
+ new_geometry = geom_num_list
248
+ for iter in range(self.maxiter):
249
+ isconverged = True
250
+ for constraint in self.constraint_condition:
251
+ if len(constraint) == 3: # bond
252
+ idx_i = constraint[1] - 1
253
+ idx_j = constraint[2] - 1
254
+ constraint_distance = constraint[0] / UnitValueLib().bohr2angstroms
255
+ r_ij = new_geometry[idx_i] - new_geometry[idx_j]
256
+ check_convergence = abs(constraint_distance - np.linalg.norm(r_ij))
257
+ if check_convergence < self.convergent_criterion:
258
+ continue
259
+ isconverged = False
260
+ prev_r_ij = prev_geom_num_list[idx_i] - prev_geom_num_list[idx_j]
261
+ g_ij = (np.linalg.norm(r_ij) ** 2 - constraint_distance ** 2) / (2 * (np.sum(r_ij * prev_r_ij)) * (1/atomic_mass(element_list[idx_i]) + 1/atomic_mass(element_list[idx_j])))
262
+ new_geometry[idx_i] -= g_ij / atomic_mass(element_list[idx_i]) * prev_r_ij
263
+ new_geometry[idx_j] += g_ij / atomic_mass(element_list[idx_j]) * prev_r_ij
264
+
265
+
266
+ elif len(constraint) == 4: # angle
267
+ # ref.:J. Chem. Phys. 133, 034114 (2010)
268
+ idx_i = constraint[1] - 1
269
+ idx_j = constraint[2] - 1
270
+ idx_k = constraint[3] - 1
271
+ constraint_angle = np.deg2rad(constraint[0])
272
+ r_ij = new_geometry[idx_i] - new_geometry[idx_j]
273
+ r_kj = new_geometry[idx_k] - new_geometry[idx_j]
274
+ inner_product_r_ij_r_kj = np.sum(r_ij * r_kj)
275
+ cos = inner_product_r_ij_r_kj / (np.linalg.norm(r_ij) * np.linalg.norm(r_kj))
276
+ constraint_cos = np.cos(constraint_angle)
277
+ check_convergence = abs(cos ** 2 - constraint_cos ** 2)
278
+ #print(check_convergence)
279
+ if check_convergence < self.convergent_criterion:
280
+
281
+ continue
282
+ isconverged = False
283
+ h_i = -2 * cos * (-1 * cos * r_ij / np.linalg.norm(r_ij) + r_kj / np.linalg.norm(r_kj)) / np.linalg.norm(r_ij) * (1.0 / atomic_mass(element_list[idx_i]))
284
+ h_k = -2 * cos * (-1 * cos * r_kj / np.linalg.norm(r_kj) + r_ij / np.linalg.norm(r_ij)) / np.linalg.norm(r_kj) * (1.0 / atomic_mass(element_list[idx_k]))
285
+ h_j = -1 * (h_i + h_k)
286
+ LAMBDA = 2 * cos * (((np.sum(-1 * r_ij * (h_j - h_k)) + np.sum(-1 * r_kj * (h_j - h_i))) / (np.linalg.norm(r_ij) * np.linalg.norm(r_kj))) -1 * ((np.sum(-1 * r_ij * (h_j - h_i)) / np.linalg.norm(r_ij) ** 2) + (np.sum(-1 * r_kj * (h_j - h_k)) / np.linalg.norm(r_kj) ** 2)) * cos)
287
+
288
+ new_geometry[idx_i] -= 1e+1 * LAMBDA * h_i
289
+ new_geometry[idx_j] -= 1e+1 * LAMBDA * h_j
290
+ new_geometry[idx_k] -= 1e+1 * LAMBDA * h_k
291
+
292
+ else: # dihedral angle
293
+ # ref.:J. Chem. Phys. 133, 034114 (2010)
294
+ idx_a = constraint[1] - 1
295
+ idx_b = constraint[2] - 1
296
+ idx_c = constraint[3] - 1
297
+ idx_d = constraint[4] - 1
298
+ constraint_dihedral_angle = np.deg2rad(constraint[0])
299
+ r_ba = new_geometry[idx_b] - new_geometry[idx_a]
300
+ r_bc = new_geometry[idx_b] - new_geometry[idx_c]
301
+ r_cd = new_geometry[idx_c] - new_geometry[idx_d]
302
+ a = r_ba -1 * (np.sum(r_ba * r_bc / np.linalg.norm(r_bc)) * r_bc / np.linalg.norm(r_bc))
303
+ b = r_cd -1 * (np.sum(r_cd * r_bc / np.linalg.norm(r_bc)) * r_bc / np.linalg.norm(r_bc))
304
+ cos = np.sum(a / np.linalg.norm(a) * b / np.linalg.norm(b))
305
+ constraint_cos = np.cos(constraint_dihedral_angle)
306
+ check_convergence = abs(cos ** 2 - constraint_cos ** 2)
307
+
308
+ if check_convergence < self.convergent_criterion:
309
+ continue
310
+ isconverged = False
311
+ h_a = 2 * cos * (1 / (np.linalg.norm(a))) * (b / np.linalg.norm(b) -1 * cos * a / np.linalg.norm(a)) * (1.0 / atomic_mass(element_list[idx_a]))
312
+ h_d = 2 * cos * (1 / (np.linalg.norm(b))) * (a / np.linalg.norm(a) -1 * cos * b / np.linalg.norm(b)) * (1.0 / atomic_mass(element_list[idx_d]))
313
+ h_b = 2 * cos * (h_a / (2 * cos) * ((np.sum(r_ba * r_bc / np.linalg.norm(r_bc)) / np.linalg.norm(r_bc)) -1) + h_d / (2 * cos) * (np.sum(r_cd * r_bc / np.linalg.norm(r_bc)) / np.linalg.norm(r_bc))) * (1.0 / atomic_mass(element_list[idx_b]))
314
+ h_c = 2 * cos * (-1 * h_d / (2 * cos) * ((np.sum(r_cd * r_bc / np.linalg.norm(r_bc)) / np.linalg.norm(r_bc)) -1) -1* h_a / (2 * cos) * (np.sum(r_ba * r_bc / np.linalg.norm(r_bc)) / np.linalg.norm(r_bc))) * (1.0 / atomic_mass(element_list[idx_c]))
315
+ cross_r_ab_r_bc = np.cross(-1*r_ba, r_bc)
316
+ cross_r_cd_h_bc = np.cross(r_cd, (h_b - h_c))
317
+ cross_h_cd_r_bc = np.cross((h_c - h_d), r_bc)
318
+ cross_r_bc_r_cd = np.cross(r_bc, r_cd)
319
+ cross_r_bc_h_ab = np.cross(r_bc, (h_a - h_b))
320
+ cross_h_bc_r_ab = np.cross((h_b - h_c), -1*r_ba)
321
+
322
+
323
+ LAMBDA = -2 * cos * (((np.sum(cross_r_ab_r_bc * (cross_r_cd_h_bc + cross_h_cd_r_bc)) + np.sum(cross_r_bc_r_cd * (cross_r_bc_h_ab + cross_h_bc_r_ab))) / (np.linalg.norm(cross_r_ab_r_bc) * np.linalg.norm(cross_r_bc_r_cd))) -1 * ((np.sum(cross_r_ab_r_bc * (cross_r_bc_h_ab + cross_h_bc_r_ab))/np.linalg.norm(cross_r_ab_r_bc) ** 2) + (np.sum(cross_r_bc_r_cd * (cross_r_cd_h_bc + cross_h_cd_r_bc))/np.linalg.norm(cross_r_bc_r_cd) ** 2)) * cos)
324
+
325
+
326
+ new_geometry[idx_a] -= 1e+3 * LAMBDA * h_a
327
+ new_geometry[idx_b] -= 1e+3 * LAMBDA * h_b
328
+ new_geometry[idx_c] -= 1e+3 * LAMBDA * h_c
329
+ new_geometry[idx_d] -= 1e+3 * LAMBDA * h_d
330
+ if isconverged:
331
+ print("converged!!! (SHAKE for energy minimalization)")
332
+ break
333
+ else:
334
+ print("not converged... (SHAKE for energy minimalization)")
335
+
336
+
337
+ return new_geometry
338
+
339
+
340
+ class ProjectOutConstrain:
341
+ def __init__(self, constraint_name, constraint_atoms_list, constraint_constant=[]):
342
+ self.constraint_name = constraint_name
343
+ self.constraint_atoms_list = []
344
+ for i in range(len(constraint_atoms_list)):
345
+ tmp_list = []
346
+ for j in range(len(constraint_atoms_list[i])):
347
+ tmp_list.append(int(constraint_atoms_list[i][j]))
348
+ self.constraint_atoms_list.append(tmp_list)
349
+
350
+ self.constraint_constant = constraint_constant
351
+ self.iteration = 1
352
+ self.init_tag = True
353
+ self.spring_const = 0.0
354
+ self.projection_vec = None
355
+ self.arbitrary_proj_vec = None
356
+ return
357
+
358
+ def initialize(self, geom_num_list, **kwargs):#Bohr
359
+ tmp_init_constraint = []
360
+ tmp_projection_vec = []
361
+ tmp_arbitrary_proj_vec = []
362
+ for i in range(len(self.constraint_name)):
363
+ if self.constraint_name[i] == "bond":
364
+ vec_1 = geom_num_list[self.constraint_atoms_list[i][0] - 1]
365
+ vec_2 = geom_num_list[self.constraint_atoms_list[i][1] - 1]
366
+ init_bond_dist = calc_bond_length_from_vec(vec_1, vec_2)
367
+ tmp_init_constraint.append(init_bond_dist)
368
+
369
+ elif self.constraint_name[i] == "fbond":
370
+ divide_index = self.constraint_atoms_list[i][-1]
371
+ fragm_1 = np.array(self.constraint_atoms_list[i][:divide_index], dtype=np.int32) - 1
372
+ fragm_2 = np.array(self.constraint_atoms_list[i][divide_index:], dtype=np.int32) - 1
373
+ vec_1 = np.mean(geom_num_list[fragm_1], axis=0)
374
+ vec_2 = np.mean(geom_num_list[fragm_2], axis=0)
375
+ init_bond_dist = calc_bond_length_from_vec(vec_1, vec_2)
376
+ tmp_init_constraint.append(init_bond_dist)
377
+
378
+ elif self.constraint_name[i] == "angle":
379
+ vec_1 = geom_num_list[self.constraint_atoms_list[i][0] - 1] - geom_num_list[self.constraint_atoms_list[i][1] - 1]
380
+ vec_2 = geom_num_list[self.constraint_atoms_list[i][2] - 1] - geom_num_list[self.constraint_atoms_list[i][1] - 1]
381
+ init_angle = calc_angle_from_vec(vec_1, vec_2)
382
+ tmp_init_constraint.append(init_angle)
383
+
384
+ elif self.constraint_name[i] == "dihedral":
385
+ vec_1 = geom_num_list[self.constraint_atoms_list[i][0] - 1] - geom_num_list[self.constraint_atoms_list[i][1] - 1]
386
+ vec_2 = geom_num_list[self.constraint_atoms_list[i][1] - 1] - geom_num_list[self.constraint_atoms_list[i][2] - 1]
387
+ vec_3 = geom_num_list[self.constraint_atoms_list[i][2] - 1] - geom_num_list[self.constraint_atoms_list[i][3] - 1]
388
+ init_dihedral = calc_dihedral_angle_from_vec(vec_1, vec_2, vec_3)
389
+ tmp_init_constraint.append(init_dihedral)
390
+
391
+ elif self.constraint_name[i] == "x":
392
+ tmp_init_constraint.append(geom_num_list[self.constraint_atoms_list[i][0] - 1][0])
393
+
394
+ elif self.constraint_name[i] == "y":
395
+ tmp_init_constraint.append(geom_num_list[self.constraint_atoms_list[i][0] - 1][1])
396
+
397
+ elif self.constraint_name[i] == "z":
398
+ tmp_init_constraint.append(geom_num_list[self.constraint_atoms_list[i][0] - 1][2])
399
+
400
+ elif self.constraint_name[i] == "rot":
401
+ tmp_init_constraint.append(geom_num_list)
402
+
403
+ elif self.constraint_name[i] == "eigvec":#This implementation is only available for "optmain.py (optimization.py)"
404
+ mode_index = int(self.constraint_atoms_list[i][0])
405
+ if "hessian" in kwargs:
406
+ hessian = copy.copy(kwargs["hessian"])
407
+ eigvals, eigvecs = np.linalg.eigh(hessian)
408
+ valid_indices = np.where(np.abs(eigvals) > 1.0e-10)[0]
409
+ sorted_indices = valid_indices[np.argsort(eigvals[valid_indices])]
410
+ target_mode = sorted_indices[mode_index]
411
+ init_eigvec = eigvecs[:, target_mode]
412
+ tmp_init_constraint.append(geom_num_list)
413
+ tmp_projection_vec.append(init_eigvec)
414
+
415
+ else:
416
+ print("error")
417
+ raise "error (Hessian is required for eigvec constraint)"
418
+
419
+ elif self.constraint_name[i] == "atoms_pair":
420
+ atom_label_1 = self.constraint_atoms_list[i][0] - 1
421
+ atom_label_2 = self.constraint_atoms_list[i][1] - 1
422
+
423
+ vec = np.zeros_like(geom_num_list)
424
+ vec[atom_label_1] = -geom_num_list[atom_label_1] + geom_num_list[atom_label_2]
425
+ vec[atom_label_2] = -geom_num_list[atom_label_2] + geom_num_list[atom_label_1]
426
+
427
+ norm_vec = np.linalg.norm(vec)
428
+ if norm_vec < 1.0e-10:
429
+ print("error")
430
+ raise "error (the distance between the pair atoms is too small)"
431
+ unit_vec = vec / norm_vec
432
+ unit_vec = unit_vec.reshape(-1, 1)
433
+ tmp_arbitrary_proj_vec.append(unit_vec)
434
+ tmp_init_constraint.append(geom_num_list)
435
+
436
+
437
+ else:
438
+ print("error")
439
+ raise "error (invaild input of constraint conditions)"
440
+
441
+ self.projection_vec = tmp_projection_vec
442
+
443
+ def gram_schmidt(vectors):
444
+ ortho = []
445
+ for v in vectors:
446
+ w = v.copy()
447
+ for u in ortho:
448
+ w -= np.dot(u.T, w) * u
449
+ norm = np.linalg.norm(w)
450
+ if norm > 1e-10:
451
+ ortho.append(w / norm)
452
+ return ortho
453
+
454
+ self.arbitrary_proj_vec = gram_schmidt(tmp_arbitrary_proj_vec)
455
+ if self.init_tag:
456
+ if len(self.constraint_constant) == 0:
457
+ self.init_constraint = tmp_init_constraint
458
+ else:
459
+ self.init_constraint = []
460
+ for i in range(len(self.constraint_constant)):
461
+ if self.constraint_name[i] == "bond" or self.constraint_name[i] == "fbond" or self.constraint_name[i] == "x" or self.constraint_name[i] == "y" or self.constraint_name[i] == "z":
462
+ self.init_constraint.append(self.constraint_constant[i] / UnitValueLib().bohr2angstroms)
463
+ elif self.constraint_name[i] == "angle" or self.constraint_name[i] == "dihedral":
464
+ self.init_constraint.append(np.deg2rad(self.constraint_constant[i]))
465
+
466
+ elif self.constraint_name[i] == "rot":
467
+ self.init_constraint.append(geom_num_list)
468
+ elif self.constraint_name[i] == "eigvec":
469
+ self.init_constraint.append(geom_num_list)
470
+ elif self.constraint_name[i] == "atoms_pair":
471
+ self.init_constraint.append(geom_num_list)
472
+ else:
473
+ print("error")
474
+ raise "error (invaild input of constraint conditions)"
475
+
476
+ self.init_tag = False
477
+
478
+ return tmp_init_constraint
479
+
480
+ def adjust_init_coord(self, coord, hessian=None):#coord:Bohr
481
+ print("Adjusting initial coordinates... (SHAKE-like method) ")
482
+ jiter = 10000
483
+ shake_like_method_threshold = 1.0e-10
484
+
485
+
486
+ for i_constrain in range(len(self.constraint_name)):
487
+
488
+ if self.constraint_name[i_constrain] == "rot":
489
+ print("fix fragment rotation... (Experimental Implementation)")
490
+ atom_label = self.constraint_atoms_list[i_constrain]
491
+ init_coord = self.init_constraint[i_constrain]
492
+ coord = rotate_partial_struct(coord, init_coord, atom_label)
493
+ elif self.constraint_name[i_constrain] == "eigvec":
494
+ print("projecting out eigenvector... (Experimental Implementation)")
495
+ init_coord = self.init_constraint[i_constrain]
496
+ coord, _ = Calculationtools().kabsch_algorithm(coord, init_coord)
497
+ elif self.constraint_name[i_constrain] == "atoms_pair":
498
+ print("projecting out translation along the vector between the pair atoms... (Experimental Implementation)")
499
+ init_coord = self.init_constraint[i_constrain]
500
+ coord, _ = Calculationtools().kabsch_algorithm(coord, init_coord)
501
+
502
+ for jter in range(jiter): # SHAKE-like algorithm
503
+ for i_constrain in range(len(self.constraint_name)):
504
+ if self.constraint_name[i_constrain] == "bond":
505
+ atom_label_1 = self.constraint_atoms_list[i_constrain][0] - 1
506
+ atom_label_2 = self.constraint_atoms_list[i_constrain][1] - 1
507
+ coord = change_atom_distance_both_side(coord, atom_label_1, atom_label_2, self.init_constraint[i_constrain])
508
+
509
+ elif self.constraint_name[i_constrain] == "fbond":
510
+ divide_index = self.constraint_atoms_list[i_constrain][-1]
511
+ fragm_1 = np.array(self.constraint_atoms_list[i_constrain][:divide_index], dtype=np.int32) - 1
512
+ fragm_2 = np.array(self.constraint_atoms_list[i_constrain][divide_index:], dtype=np.int32) - 1
513
+ coord = change_fragm_distance_both_side(coord, fragm_1, fragm_2, self.init_constraint[i_constrain])
514
+
515
+ elif self.constraint_name[i_constrain] == "angle":
516
+ atom_label_1 = self.constraint_atoms_list[i_constrain][0] - 1
517
+ atom_label_2 = self.constraint_atoms_list[i_constrain][1] - 1
518
+ atom_label_3 = self.constraint_atoms_list[i_constrain][2] - 1
519
+ coord = change_bond_angle_both_side(coord, atom_label_1, atom_label_2, atom_label_3, self.init_constraint[i_constrain])
520
+
521
+ elif self.constraint_name[i_constrain] == "dihedral":
522
+ atom_label_1 = self.constraint_atoms_list[i_constrain][0] - 1
523
+ atom_label_2 = self.constraint_atoms_list[i_constrain][1] - 1
524
+ atom_label_3 = self.constraint_atoms_list[i_constrain][2] - 1
525
+ atom_label_4 = self.constraint_atoms_list[i_constrain][3] - 1
526
+ coord = change_torsion_angle_both_side(coord, atom_label_1, atom_label_2, atom_label_3, atom_label_4, self.init_constraint[i_constrain])
527
+
528
+ elif self.constraint_name[i_constrain] == "x":
529
+ atom_label = self.constraint_atoms_list[i_constrain][0] - 1
530
+ coord[atom_label][0] = self.init_constraint[i_constrain]
531
+
532
+
533
+ elif self.constraint_name[i_constrain] == "y":
534
+ atom_label = self.constraint_atoms_list[i_constrain][0] - 1
535
+ coord[atom_label][1] = self.init_constraint[i_constrain]
536
+
537
+ elif self.constraint_name[i_constrain] == "z":
538
+ atom_label = self.constraint_atoms_list[i_constrain][0] - 1
539
+ coord[atom_label][2] = self.init_constraint[i_constrain]
540
+
541
+
542
+ else:
543
+ pass
544
+
545
+ tmp_current_coord = self.initialize(coord, hessian=hessian)
546
+ current_coord = []
547
+ tmp_init_constraint = []
548
+ for i_constrain in range(len(self.constraint_name)):
549
+ if self.constraint_name[i_constrain] != "rot" and self.constraint_name[i_constrain] != "eigvec" and self.constraint_name[i_constrain] != "atoms_pair":
550
+ current_coord.append(tmp_current_coord[i_constrain])
551
+ tmp_init_constraint.append(self.init_constraint[i_constrain])
552
+
553
+
554
+ current_coord = np.array(current_coord)
555
+ tmp_init_constraint = np.array(tmp_init_constraint)
556
+ if np.linalg.norm(current_coord - tmp_init_constraint) < shake_like_method_threshold:
557
+ print("Adjusted!!! : ITR. ", jter)
558
+ break
559
+
560
+
561
+
562
+ return coord
563
+
564
+
565
+
566
+
567
+ def calc_project_out_grad(self, coord, grad):# grad: (3N, 1), geom_num_list: (N, 3)
568
+ natom = len(coord)
569
+ tmp_grad = copy.copy(grad)
570
+ tmp_b_mat = None
571
+ B_mat = None
572
+ projection_vec_count = 0
573
+ arbitrary_vec_count = 0
574
+ for i_constrain in range(len(self.constraint_name)):
575
+ if self.constraint_name[i_constrain] == "bond":
576
+ print("Projecting out bond... ")
577
+ atom_label = [self.constraint_atoms_list[i_constrain][0], self.constraint_atoms_list[i_constrain][1]]
578
+ print("atom_label:", atom_label)
579
+ tmp_b_mat = torch_B_matrix(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_distance).detach().numpy().reshape(1, -1)
580
+
581
+ elif self.constraint_name[i_constrain] == "fbond":
582
+ print("Projecting out fragment bond... (Experimental Implementation)")
583
+ divide_index = self.constraint_atoms_list[i_constrain][-1]
584
+ fragm_1 = torch.tensor(self.constraint_atoms_list[i_constrain][:divide_index], dtype=torch.int64)
585
+ fragm_2 = torch.tensor(self.constraint_atoms_list[i_constrain][divide_index:], dtype=torch.int64)
586
+ atom_label = [fragm_1, fragm_2]
587
+ print("atom_label:", atom_label)
588
+ tmp_b_mat = torch_B_matrix(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_fragm_distance).detach().numpy().reshape(1, -1)
589
+
590
+ elif self.constraint_name[i_constrain] == "angle":
591
+ print("Projecting out bond angle... ")
592
+ atom_label = [self.constraint_atoms_list[i_constrain][0], self.constraint_atoms_list[i_constrain][1], self.constraint_atoms_list[i_constrain][2]]
593
+ print("atom_label:", atom_label)
594
+ tmp_b_mat = torch_B_matrix(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_angle).detach().numpy().reshape(1, -1)
595
+
596
+ elif self.constraint_name[i_constrain] == "dihedral":
597
+ print("Projecting out dihedral angle... ")
598
+ atom_label = [self.constraint_atoms_list[i_constrain][0], self.constraint_atoms_list[i_constrain][1], self.constraint_atoms_list[i_constrain][2], self.constraint_atoms_list[i_constrain][3]]
599
+ print("atom_label:", atom_label)
600
+ tmp_b_mat = torch_B_matrix(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_dihedral_angle).detach().numpy().reshape(1, -1)
601
+
602
+ elif self.constraint_name[i_constrain] == "x":
603
+ print("Projecting out x coordinate... ")
604
+ atom_label = self.constraint_atoms_list[i_constrain][0]
605
+ print("atom_label:", atom_label)
606
+ tmp_b_mat = torch.zeros(1, 3*natom)
607
+ tmp_b_mat[0][3*(atom_label - 1) + 0] = 1.0
608
+
609
+ elif self.constraint_name[i_constrain] == "y":
610
+ print("Projecting out y coordinate... ")
611
+ tmp_b_mat = torch.zeros(1, 3*natom)
612
+
613
+ atom_label = self.constraint_atoms_list[i_constrain][0]
614
+ print("atom_label:", atom_label)
615
+ tmp_b_mat[0][3*(atom_label - 1) + 1] = 1.0
616
+
617
+ elif self.constraint_name[i_constrain] == "z":
618
+ print("Projecting out z coordinate... ")
619
+ tmp_b_mat = torch.zeros(1, 3*natom)
620
+ atom_label = self.constraint_atoms_list[i_constrain][0]
621
+ print("atom_label:", atom_label)
622
+ tmp_b_mat[0][3*(atom_label - 1) + 2] = 1.0
623
+ elif self.constraint_name[i_constrain] == "rot":
624
+ print("Projecting out fragment rotation... (Experimental Implementation)")
625
+ atom_label = self.constraint_atoms_list[i_constrain]
626
+ print("atom_label:", atom_label)
627
+ tmp_b_mat = constract_partial_rot_B_mat(coord, atom_label)
628
+
629
+ elif self.constraint_name[i_constrain] == "eigvec":
630
+ print("Projecting out eigenvector... (Experimental Implementation)")
631
+ tmp_proj_vec = self.projection_vec[projection_vec_count]
632
+ print("mode index:", self.constraint_atoms_list[i_constrain][0])
633
+ projection_vec_count += 1
634
+ tmp_grad = np.dot((np.eye(len(tmp_proj_vec)) - np.outer(tmp_proj_vec, tmp_proj_vec)), tmp_grad.reshape(3*natom, 1))
635
+ tmp_b_mat = None
636
+ elif self.constraint_name[i_constrain] == "atoms_pair":
637
+ if len(self.arbitrary_proj_vec) < arbitrary_vec_count + 1:
638
+ pass
639
+ else:
640
+ print("Projecting out translation along the vector between the pair atoms... (Experimental Implementation)")
641
+ tmp_arbitrary_proj_vec = self.arbitrary_proj_vec[arbitrary_vec_count]
642
+ print("atom labels:", self.constraint_atoms_list[i_constrain])
643
+ arbitrary_vec_count += 1
644
+ tmp_grad = np.dot((np.eye(len(tmp_arbitrary_proj_vec)) - np.outer(tmp_arbitrary_proj_vec, tmp_arbitrary_proj_vec)), tmp_grad.reshape(3*natom, 1))
645
+ tmp_b_mat = None
646
+ else:
647
+ print("error")
648
+ raise "error (invaild input of constraint conditions)"
649
+
650
+ if tmp_b_mat is not None:
651
+ if 'B_mat' not in locals() or B_mat is None:
652
+ B_mat = tmp_b_mat
653
+ else:
654
+ B_mat = np.vstack((B_mat, tmp_b_mat))
655
+
656
+ if B_mat is None:
657
+ return tmp_grad.reshape(natom, 3)
658
+ int_grad = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat)
659
+ projection_grad = calc_cart_grad_from_pBmat(-1*int_grad, B_mat)
660
+ proj_grad = tmp_grad.reshape(3*natom, 1) + projection_grad
661
+ proj_grad = proj_grad.reshape(natom, 3)
662
+
663
+ return proj_grad
664
+
665
+ def calc_project_out_hess(self, coord, grad, hessian):# hessian:(3N, 3N), B_g: (3N, 1), geom_num_list: (N, 3)
666
+ natom = len(coord)
667
+ tmp_grad = copy.copy(grad)
668
+ tmp_hessian = copy.copy(hessian)
669
+ tmp_b_mat = None
670
+ tmp_b_mat_1st_derivative = None
671
+ projection_vec_count = 0
672
+ arbitrary_vec_count = 0
673
+
674
+ # --- FIX: Calculate grad_rms from the ORIGINAL gradient ---
675
+ grad_rms = np.sqrt(np.mean(grad**2))
676
+ grad_rms_threshold = 1.0e-3
677
+ # ---------------------------------------------------------
678
+
679
+ for i_constrain in range(len(self.constraint_name)):
680
+ if self.constraint_name[i_constrain] == "bond":
681
+ atom_label = [self.constraint_atoms_list[i_constrain][0], self.constraint_atoms_list[i_constrain][1]]
682
+ tmp_b_mat = torch_B_matrix(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_distance).detach().numpy().reshape(1, -1)
683
+ tmp_b_mat_1st_derivative = torch_B_matrix_derivative(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_distance).detach().numpy()
684
+
685
+
686
+ elif self.constraint_name[i_constrain] == "fbond":
687
+ divide_index = self.constraint_atoms_list[i_constrain][-1]
688
+ fragm_1 = torch.tensor(self.constraint_atoms_list[i_constrain][:divide_index], dtype=torch.int64)
689
+ fragm_2 = torch.tensor(self.constraint_atoms_list[i_constrain][divide_index:], dtype=torch.int64)
690
+ atom_label = [fragm_1, fragm_2]
691
+ tmp_b_mat = torch_B_matrix(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_fragm_distance).detach().numpy().reshape(1, -1)
692
+ tmp_b_mat_1st_derivative = torch_B_matrix_derivative(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_fragm_distance).detach().numpy()
693
+
694
+
695
+ elif self.constraint_name[i_constrain] == "angle":
696
+ atom_label = [self.constraint_atoms_list[i_constrain][0], self.constraint_atoms_list[i_constrain][1], self.constraint_atoms_list[i_constrain][2]]
697
+ tmp_b_mat = torch_B_matrix(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_angle).detach().numpy().reshape(1, -1)
698
+ tmp_b_mat_1st_derivative = torch_B_matrix_derivative(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_angle).detach().numpy()
699
+
700
+ elif self.constraint_name[i_constrain] == "dihedral":
701
+ atom_label = [self.constraint_atoms_list[i_constrain][0], self.constraint_atoms_list[i_constrain][1], self.constraint_atoms_list[i_constrain][2], self.constraint_atoms_list[i_constrain][3]]
702
+ tmp_b_mat = torch_B_matrix(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_dihedral_angle).detach().numpy().reshape(1, -1)
703
+ tmp_b_mat_1st_derivative = torch_B_matrix_derivative(torch.tensor(coord, dtype=torch.float64), atom_label, torch_calc_dihedral_angle).detach().numpy()
704
+
705
+ elif self.constraint_name[i_constrain] == "x":
706
+ atom_label = self.constraint_atoms_list[i_constrain][0]
707
+ tmp_b_mat = torch.zeros(1, 3*natom)
708
+ tmp_b_mat[0][3*(atom_label - 1) + 0] = 1.0
709
+ tmp_b_mat_1st_derivative = torch.zeros_like(torch_B_matrix_derivative(torch.tensor(coord, dtype=torch.float64), [atom_label,atom_label], torch_calc_distance)).detach().numpy()
710
+
711
+ elif self.constraint_name[i_constrain] == "y":
712
+ atom_label = self.constraint_atoms_list[i_constrain][0]
713
+ tmp_b_mat = torch.zeros(1, 3*natom)
714
+ tmp_b_mat[0][3*(atom_label - 1) + 1] = 1.0
715
+ tmp_b_mat_1st_derivative = torch.zeros_like(torch_B_matrix_derivative(torch.tensor(coord, dtype=torch.float64), [atom_label,atom_label], torch_calc_distance)).detach().numpy()
716
+
717
+ elif self.constraint_name[i_constrain] == "z":
718
+ atom_label = self.constraint_atoms_list[i_constrain][0]
719
+ tmp_b_mat = torch.zeros(1, 3*natom)
720
+ tmp_b_mat[0][3*(atom_label - 1) + 2] = 1.0
721
+ tmp_b_mat_1st_derivative = torch.zeros_like(torch_B_matrix_derivative(torch.tensor(coord, dtype=torch.float64), [atom_label,atom_label], torch_calc_distance)).detach().numpy()
722
+
723
+ elif self.constraint_name[i_constrain] == "rot":
724
+ tmp_b_mat_1st_derivative = None
725
+ ## atom_label = self.constraint_atoms_list[i_constrain]
726
+ # tmp_b_mat = constract_partial_rot_B_mat(coord, atom_label)
727
+ # tmp_b_mat_1st_derivative = torch.zeros_like(torch_B_matrix_derivative(torch.tensor(coord, dtype=torch.float64), [atom_label[0],atom_label[0]], torch_calc_distance)).detach().numpy() #TODO: implement this function
728
+
729
+ elif self.constraint_name[i_constrain] == "eigvec":
730
+ tmp_proj_vec = self.projection_vec[projection_vec_count]
731
+ projection_vec_count += 1
732
+ tmp_grad = np.dot((np.eye(len(tmp_proj_vec)) - np.outer(tmp_proj_vec, tmp_proj_vec)), tmp_grad.reshape(3*natom, 1))
733
+ tmp_hessian = np.dot((np.eye(len(tmp_proj_vec)) - np.outer(tmp_proj_vec, tmp_proj_vec)), tmp_hessian)
734
+ tmp_b_mat_1st_derivative = None
735
+ elif self.constraint_name[i_constrain] == "atoms_pair":
736
+ tmp_arbitrary_proj_vec = self.arbitrary_proj_vec[arbitrary_vec_count]
737
+ arbitrary_vec_count += 1
738
+ tmp_grad = np.dot((np.eye(len(tmp_arbitrary_proj_vec)) - np.outer(tmp_arbitrary_proj_vec, tmp_arbitrary_proj_vec)), tmp_grad.reshape(3*natom, 1))
739
+ tmp_hessian = np.dot(np.dot((np.eye(len(tmp_arbitrary_proj_vec)) - np.outer(tmp_arbitrary_proj_vec, tmp_arbitrary_proj_vec)), tmp_hessian), (np.eye(len(tmp_arbitrary_proj_vec)) - np.outer(tmp_arbitrary_proj_vec, tmp_arbitrary_proj_vec)).T)
740
+ tmp_b_mat_1st_derivative = None
741
+
742
+ else:
743
+ print("error")
744
+ raise "error (invaild input of constraint conditions)"
745
+
746
+
747
+ if i_constrain == 0:
748
+ B_mat = tmp_b_mat
749
+ B_mat_1st_derivative = tmp_b_mat_1st_derivative
750
+ else:
751
+ # FIX: Handle vstack/concatenate when B_mat or B_mat_1st_derivative is None
752
+ # (e.g., if 'eigvec' is the first constraint)
753
+ if B_mat is None:
754
+ B_mat = tmp_b_mat
755
+ elif tmp_b_mat is not None:
756
+ B_mat = np.vstack((B_mat, tmp_b_mat))
757
+
758
+ if B_mat_1st_derivative is None:
759
+ B_mat_1st_derivative = tmp_b_mat_1st_derivative
760
+ elif tmp_b_mat_1st_derivative is not None:
761
+ B_mat_1st_derivative = np.concatenate((B_mat_1st_derivative, tmp_b_mat_1st_derivative), axis=2)
762
+
763
+
764
+ # --- FIX: Main projection logic based on RMS and B_mat existence ---
765
+
766
+ # Case 1: No B-matrix constraints were added (e.g., only 'eigvec'/'atoms_pair')
767
+ if B_mat is None:
768
+ proj_hess = tmp_hessian
769
+
770
+ # Case 2: B-matrix exists, but the *last* constraint had no B'' (e.g., 'rot'/'eigvec' was last)
771
+ elif tmp_b_mat_1st_derivative is None:
772
+ proj_hess = tmp_hessian
773
+
774
+ # Case 3: B-matrix and B'' (for the last constraint) exist
775
+ else:
776
+ # Case 3a: Gradient RMS is low (Stationary Point)
777
+ if grad_rms < grad_rms_threshold:
778
+ print(f"Gradient RMS ({grad_rms:.2e}) < {grad_rms_threshold:.2e}. Using stationary projection logic (fallback).")
779
+ # Fallback to the 'eigvec'-projected Hessian, per "mix-in" logic
780
+ proj_hess = tmp_hessian
781
+
782
+ # Case 3b: Gradient RMS is high (Non-Stationary Point)
783
+ else:
784
+ print(f"Gradient RMS ({grad_rms:.2e}) >= {grad_rms_threshold:.2e}. Using non-stationary projection.")
785
+ # Use the 'eigvec'-projected tmp_grad for int_grad calculation
786
+ int_grad = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat)
787
+ # Use the 'eigvec'-projected tmp_hessian for non-stationary calculation
788
+ proj_hess = tmp_hessian
789
+ int_hess = calc_int_hess_from_pBmat_for_non_stationary_point(tmp_hessian, B_mat, B_mat_1st_derivative, int_grad)
790
+ couple_hess = calc_int_cart_coupling_hess_from_pBmat_for_non_stationary_point(tmp_hessian, B_mat, B_mat_1st_derivative, int_grad)
791
+ #hess_x = calc_cart_hess_from_pBmat_for_non_stationary_point(tmp_hessian, B_mat, B_mat_1st_derivative, int_grad)
792
+ try:
793
+ int_hess_inv = np.linalg.pinv(int_hess)
794
+ except np.linalg.LinAlgError:
795
+ int_hess = int_hess + np.eye(len(int_hess)) * 1e-10
796
+ int_hess_inv = np.linalg.pinv(int_hess)
797
+ eff_hess = np.dot(couple_hess.T, np.dot(int_hess_inv, couple_hess))
798
+ proj_hess = proj_hess - eff_hess
799
+
800
+ return proj_hess
801
+
802
+ def constract_partial_rot_B_mat(geom_num_list, target_atoms_list):#1-based index
803
+ target_atoms_list = np.array(target_atoms_list, dtype=np.int32) - 1
804
+ center = np.mean(geom_num_list[target_atoms_list], axis=0)
805
+ centroid_geom_num_list = geom_num_list[target_atoms_list] - center
806
+ B_mat = np.zeros((3 * len(target_atoms_list), 3 * len(geom_num_list)))
807
+ for j in range(len(target_atoms_list)):
808
+ i = target_atoms_list[j]
809
+
810
+ B_mat[3*j][3*i+0] = 0.0
811
+ B_mat[3*j][3*i+1] = centroid_geom_num_list[i][2]
812
+ B_mat[3*j][3*i+2] = -1 * centroid_geom_num_list[i][1]
813
+ B_mat[3*j+1][3*i+0] = -1 * centroid_geom_num_list[i][2]
814
+ B_mat[3*j+1][3*i+1] = 0.0
815
+ B_mat[3*j+1][3*i+2] = centroid_geom_num_list[i][0]
816
+ B_mat[3*j+2][3*i+0] = centroid_geom_num_list[i][1]
817
+ B_mat[3*j+2][3*i+1] = -1 * centroid_geom_num_list[i][0]
818
+ B_mat[3*j+2][3*i+2] = 0.0
819
+
820
+ return B_mat
821
+
822
+ def constract_partial_rot_B_mat_1st_derivative(geom_num_list, target_atoms_list):#1-based index
823
+ return
824
+
825
+ def rotate_partial_struct(geom_num_list, init_geom_num_list, target_atoms_list):#1-based index
826
+ target_atoms_list = np.array(target_atoms_list, dtype=np.int32) - 1
827
+ center = np.mean(geom_num_list[target_atoms_list], axis=0)
828
+
829
+ partial_geom_num_list = geom_num_list[target_atoms_list]
830
+ init_partial_geom_num_list = init_geom_num_list[target_atoms_list]
831
+ rotated_partial_geom_num_list, _ = Calculationtools().kabsch_algorithm(partial_geom_num_list, init_partial_geom_num_list)
832
+ rotated_partial_geom_num_list = rotated_partial_geom_num_list + center
833
+ geom_num_list[target_atoms_list] = rotated_partial_geom_num_list
834
+ return geom_num_list