Skip to content

Commit f3ea199

Browse files
authored
Add files via upload
1 parent 00cb68e commit f3ea199

1 file changed

Lines changed: 43 additions & 1 deletion

File tree

multioptpy/ModelHessian/o1numhess.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,36 @@ def _get_gradient(self, coords_ang):
9292

9393
return np.array(gradient)
9494

95+
def _guess_topology_protection(self, distmat_bohr):
96+
"""
97+
Guess 1-2 (bond) and 1-3 (angle) connectivity based on distances.
98+
Returns a boolean mask of pairs that should be protected.
99+
"""
100+
N = distmat_bohr.shape[0]
101+
102+
# 1. Define bonding threshold (e.g., 1.3 * sum of covalent radii)
103+
# Using broadcasting for radii sums
104+
radii_sum = self.atom_radii[:, np.newaxis] + self.atom_radii[np.newaxis, :]
105+
bond_thresh = 1.3 * radii_sum
106+
107+
# 2. Identify 1-2 bonds (exclude self-loop)
108+
# distmat < threshold AND distmat > 1e-3 (avoid self)
109+
bond_adj = (distmat_bohr < bond_thresh) & (distmat_bohr > 1e-3)
110+
111+
# 3. Identify 1-3 angles using matrix multiplication
112+
# If A-B is bonded and B-C is bonded, (bond_adj @ bond_adj)[A, C] > 0
113+
# converting to float for matmul, then back to bool
114+
bond_float = bond_adj.astype(float)
115+
angle_adj = (np.dot(bond_float, bond_float)) > 0.1
116+
117+
# Remove self-loops (A-B-A) introduced by matmul
118+
np.fill_diagonal(angle_adj, False)
119+
120+
# 4. Combine masks (Protects both Bonds and Angles)
121+
protected_mask = bond_adj | angle_adj
122+
123+
return protected_mask
124+
95125
def compute_hessian(self, current_coords_ang):
96126
"""
97127
Execute the O1NumHess algorithm.
@@ -112,7 +142,7 @@ def compute_hessian(self, current_coords_ang):
112142

113143
if self.verbosity > 0:
114144
print(f"\n=== Starting O1NumHess Calculation ===")
115-
print(f" Adaptive Cutoff Strategy: {self.rcov_scale} * (R_i + R_j) + 1.0")
145+
print(f" Adaptive Cutoff Strategy: {self.rcov_scale} * (R_i + R_j) + 1.0 Bohr")
116146
print(f" Min Cutoff: {np.min(cutoff_mat):.2f} Bohr, Max Cutoff: {np.max(cutoff_mat):.2f} Bohr")
117147
print(f" Step size (eta): {self.delta} Bohr")
118148

@@ -132,6 +162,18 @@ def compute_hessian(self, current_coords_ang):
132162
print(" [3/6] Building connectivity graph...")
133163
distmat_bohr = cdist(coords_bohr, coords_bohr)
134164

165+
if self.verbosity > 0:
166+
print(" -> Applying topology-based protection (1-2 & 1-3 pairs)...")
167+
168+
protected_mask = self._guess_topology_protection(distmat_bohr)
169+
170+
safe_margin = 2.0 # Bohr
171+
172+
# update only where protection is needed AND current cutoff is too small
173+
cutoff_mat[protected_mask] = np.maximum(
174+
cutoff_mat[protected_mask],
175+
distmat_bohr[protected_mask] + safe_margin
176+
)
135177
# FIXED: Pass cutoff_mat instead of scalar dmax
136178
nblist, nbcounts = self._build_neighbor_list(N_atom, distmat_bohr, cutoff_mat)
137179

0 commit comments

Comments
 (0)