Skip to content

Commit 5e36913

Browse files
authored
Add files via upload
1 parent ba0e0b8 commit 5e36913

1 file changed

Lines changed: 40 additions & 59 deletions

File tree

multioptpy/Calculator/tblite_calculation_tools.py

Lines changed: 40 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(self, **kwarg):
3636
self.START_FILE = kwarg.get("START_FILE", None)
3737
self.N_THREAD = kwarg.get("N_THREAD", 1)
3838
self.SET_MEMORY = kwarg.get("SET_MEMORY", "2GB")
39-
self.FUNCTIONAL = kwarg.get("FUNCTIONAL", None) # Used for method selection if needed
39+
self.FUNCTIONAL = kwarg.get("FUNCTIONAL", None)
4040
self.FC_COUNT = kwarg.get("FC_COUNT", 1)
4141
self.BPA_FOLDER_DIRECTORY = kwarg.get("BPA_FOLDER_DIRECTORY", "./")
4242
self.Model_hess = kwarg.get("Model_hess", None)
@@ -45,14 +45,19 @@ def __init__(self, **kwarg):
4545
self.hessian_flag = False
4646
self.cpcm_solv_model = kwarg.get("cpcm_solv_model", None)
4747
self.alpb_solv_model = kwarg.get("alpb_solv_model", None)
48-
self.method = kwarg.get("method", "GFN2-xTB") # Default method
48+
self.method = kwarg.get("method", "GFN2-xTB")
4949
self.verbosity = 0
5050
self.numerical_delivative_delta = 0.0001
5151

52+
# --- OPTIMIZATION: Enforce Thread Count for tblite/OpenMP ---
53+
os.environ["OMP_NUM_THREADS"] = str(self.N_THREAD)
54+
os.environ["MKL_NUM_THREADS"] = str(self.N_THREAD)
55+
os.environ["OPENBLAS_NUM_THREADS"] = str(self.N_THREAD)
56+
5257
def _get_calculator(self, positions_bohr, element_numbers, charge, uhf):
5358
"""Internal helper to create Calculator instance"""
5459
calc = Calculator(self.method, element_numbers, positions_bohr, charge=charge, uhf=uhf)
55-
calc.set("max-iter", 500) # Or dynamic based on atoms
60+
calc.set("max-iter", 500)
5661
calc.set("verbosity", self.verbosity)
5762

5863
if self.cpcm_solv_model is not None:
@@ -65,92 +70,68 @@ def _get_calculator(self, positions_bohr, element_numbers, charge, uhf):
6570
def run_calculation(self, positions_bohr, element_number_list, charge_mult):
6671
"""
6772
Execute TBLite calculation for a single geometry.
68-
69-
Args:
70-
positions_bohr (np.ndarray): Coordinates in Bohr.
71-
element_number_list (np.ndarray): Array of atomic numbers.
72-
charge_mult (list): [charge, multiplicity].
73-
74-
Returns:
75-
tuple: (energy, gradient)
7673
"""
7774
charge = int(charge_mult[0])
7875
mult = int(charge_mult[1])
79-
# xTB logic: uhf is number of unpaired electrons (mult - 1)
8076
uhf = max(mult - 1, 0)
8177

82-
# Override uhf if unrestrict flag is set but mult is 1 (though usually 0 for singlet)
8378
if self.unrestrict and uhf == 0:
84-
# Logic depends on specific needs, usually 0 is fine for RHF/RKS equivalent
8579
pass
8680

8781
calc = self._get_calculator(positions_bohr, element_number_list, charge, uhf)
8882

89-
# Execute
9083
res = calc.singlepoint()
9184
e = float(res.get("energy"))
9285
g = res.get("gradient") # Hartree/Bohr
9386

94-
np.save(self.BPA_FOLDER_DIRECTORY+"raw_grad.npy", g)
95-
96-
self.res = res # Store full result for later access to orbitals, etc.
87+
np.save(os.path.join(self.BPA_FOLDER_DIRECTORY, "raw_grad.npy"), g)
88+
self.res = res
9789

9890
return e, g
9991

10092
def numerical_hessian(self, geom_num_list, element_number_list, electric_charge_and_multiplicity):
10193
"""
10294
Compute numerical Hessian using finite difference of gradients.
95+
(Highly optimized O(N) evaluation with 1D loop and in-place modification)
10396
"""
10497
n_atoms = len(geom_num_list)
10598
hessian = np.zeros((3 * n_atoms, 3 * n_atoms))
10699

107100
charge = int(electric_charge_and_multiplicity[0])
108-
uhf = int(electric_charge_and_multiplicity[1]) - 1
109-
110-
# Pre-calculation setup
111-
# Note: Optimization opportunity - parallelize this loop if possible in future
112-
count = 0
113-
for atom_num in range(n_atoms):
114-
for i in range(3): # x, y, z
115-
for atom_num_2 in range(n_atoms):
116-
for j in range(3):
117-
# Symmetric check to avoid double calculation
118-
if count > 3 * atom_num_2 + j:
119-
continue
120-
121-
tmp_grad = []
122-
for direction in [1, -1]:
123-
# Perturb geometry
124-
perturbed_geom = copy.copy(geom_num_list)
125-
perturbed_geom[atom_num][i] += direction * self.numerical_delivative_delta
126-
127-
calc = self._get_calculator(perturbed_geom, element_number_list, charge, uhf)
128-
res = calc.singlepoint()
129-
g = res.get("gradient")
130-
tmp_grad.append(g[atom_num_2][j])
131-
132-
# Central difference
133-
val = (tmp_grad[0] - tmp_grad[1]) / (2 * self.numerical_delivative_delta)
134-
hessian[3*atom_num+i][3*atom_num_2+j] = val
135-
hessian[3*atom_num_2+j][3*atom_num+i] = val
136-
137-
count += 1
101+
uhf = max(int(electric_charge_and_multiplicity[1]) - 1, 0)
102+
delta = self.numerical_delivative_delta
103+
104+
105+
geom = np.array(geom_num_list, dtype=float)
106+
107+
for j in range(3 * n_atoms):
108+
atom = j // 3
109+
coord = j % 3
110+
111+
112+
geom[atom, coord] += delta
113+
calc_plus = self._get_calculator(geom, element_number_list, charge, uhf)
114+
grad_plus = calc_plus.singlepoint().get("gradient").flatten()
115+
116+
117+
geom[atom, coord] -= 2 * delta
118+
calc_minus = self._get_calculator(geom, element_number_list, charge, uhf)
119+
grad_minus = calc_minus.singlepoint().get("gradient").flatten()
120+
121+
122+
geom[atom, coord] += delta
123+
124+
125+
hessian[:, j] = (grad_plus - grad_minus) / (2 * delta)
126+
127+
128+
hessian = (hessian + hessian.T) / 2.0
138129
return hessian
139130

140131
def calc_exact_hess(self, positions_bohr, element_number_list, charge_mult):
141-
"""
142-
Calculate Exact Hessian (Numerical) and project out TR modes.
143-
"""
144-
# Compute raw numerical Hessian
145132
exact_hess = self.numerical_hessian(positions_bohr, element_number_list, charge_mult)
146-
np.save(self.BPA_FOLDER_DIRECTORY+"raw_hessian.npy", exact_hess)
147-
# Project out Translation/Rotation
148-
# Note: Calculationtools usually expects Bohr for projection if hessian is in au?
149-
# Re-checking previous context: `project_out_hess_tr_and_rot_for_coord` seems to handle it.
150-
# It takes `positions` which here are in Bohr.
133+
np.save(os.path.join(self.BPA_FOLDER_DIRECTORY, "raw_hessian.npy"), exact_hess)
151134

152-
# Ensure element_number_list is a list for the tool if needed, or array
153-
# The original code passed `element_number_list.tolist()`
154135
elem_list_arg = element_number_list.tolist() if isinstance(element_number_list, np.ndarray) else element_number_list
155136

156137
exact_hess = Calculationtools().project_out_hess_tr_and_rot_for_coord(

0 commit comments

Comments
 (0)