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.
- multioptpy/Calculator/__init__.py +0 -0
- multioptpy/Calculator/ase_calculation_tools.py +424 -0
- multioptpy/Calculator/ase_tools/__init__.py +0 -0
- multioptpy/Calculator/ase_tools/fairchem.py +28 -0
- multioptpy/Calculator/ase_tools/gamess.py +19 -0
- multioptpy/Calculator/ase_tools/gaussian.py +165 -0
- multioptpy/Calculator/ase_tools/mace.py +28 -0
- multioptpy/Calculator/ase_tools/mopac.py +19 -0
- multioptpy/Calculator/ase_tools/nwchem.py +31 -0
- multioptpy/Calculator/ase_tools/orca.py +22 -0
- multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
- multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
- multioptpy/Calculator/emt_calculation_tools.py +458 -0
- multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
- multioptpy/Calculator/lj_calculation_tools.py +314 -0
- multioptpy/Calculator/psi4_calculation_tools.py +334 -0
- multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
- multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
- multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
- multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
- multioptpy/Calculator/tblite_calculation_tools.py +352 -0
- multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
- multioptpy/Constraint/__init__.py +0 -0
- multioptpy/Constraint/constraint_condition.py +834 -0
- multioptpy/Coordinate/__init__.py +0 -0
- multioptpy/Coordinate/polar_coordinate.py +199 -0
- multioptpy/Coordinate/redundant_coordinate.py +638 -0
- multioptpy/IRC/__init__.py +0 -0
- multioptpy/IRC/converge_criteria.py +28 -0
- multioptpy/IRC/dvv.py +544 -0
- multioptpy/IRC/euler.py +439 -0
- multioptpy/IRC/hpc.py +564 -0
- multioptpy/IRC/lqa.py +540 -0
- multioptpy/IRC/modekill.py +662 -0
- multioptpy/IRC/rk4.py +579 -0
- multioptpy/Interpolation/__init__.py +0 -0
- multioptpy/Interpolation/adaptive_interpolation.py +283 -0
- multioptpy/Interpolation/binomial_interpolation.py +179 -0
- multioptpy/Interpolation/geodesic_interpolation.py +785 -0
- multioptpy/Interpolation/interpolation.py +156 -0
- multioptpy/Interpolation/linear_interpolation.py +473 -0
- multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
- multioptpy/Interpolation/spline_interpolation.py +353 -0
- multioptpy/MD/__init__.py +0 -0
- multioptpy/MD/thermostat.py +185 -0
- multioptpy/MEP/__init__.py +0 -0
- multioptpy/MEP/pathopt_bneb_force.py +443 -0
- multioptpy/MEP/pathopt_dmf_force.py +448 -0
- multioptpy/MEP/pathopt_dneb_force.py +130 -0
- multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
- multioptpy/MEP/pathopt_gpneb_force.py +512 -0
- multioptpy/MEP/pathopt_lup_force.py +113 -0
- multioptpy/MEP/pathopt_neb_force.py +225 -0
- multioptpy/MEP/pathopt_nesb_force.py +205 -0
- multioptpy/MEP/pathopt_om_force.py +153 -0
- multioptpy/MEP/pathopt_qsm_force.py +174 -0
- multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
- multioptpy/ModelFunction/__init__.py +7 -0
- multioptpy/ModelFunction/avoiding_model_function.py +29 -0
- multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
- multioptpy/ModelFunction/conical_model_function.py +26 -0
- multioptpy/ModelFunction/opt_meci.py +50 -0
- multioptpy/ModelFunction/opt_mesx.py +47 -0
- multioptpy/ModelFunction/opt_mesx_2.py +49 -0
- multioptpy/ModelFunction/seam_model_function.py +27 -0
- multioptpy/ModelHessian/__init__.py +0 -0
- multioptpy/ModelHessian/approx_hessian.py +147 -0
- multioptpy/ModelHessian/calc_params.py +227 -0
- multioptpy/ModelHessian/fischer.py +236 -0
- multioptpy/ModelHessian/fischerd3.py +360 -0
- multioptpy/ModelHessian/fischerd4.py +398 -0
- multioptpy/ModelHessian/gfn0xtb.py +633 -0
- multioptpy/ModelHessian/gfnff.py +709 -0
- multioptpy/ModelHessian/lindh.py +165 -0
- multioptpy/ModelHessian/lindh2007d2.py +707 -0
- multioptpy/ModelHessian/lindh2007d3.py +822 -0
- multioptpy/ModelHessian/lindh2007d4.py +1030 -0
- multioptpy/ModelHessian/morse.py +106 -0
- multioptpy/ModelHessian/schlegel.py +144 -0
- multioptpy/ModelHessian/schlegeld3.py +322 -0
- multioptpy/ModelHessian/schlegeld4.py +559 -0
- multioptpy/ModelHessian/shortrange.py +346 -0
- multioptpy/ModelHessian/swartd2.py +496 -0
- multioptpy/ModelHessian/swartd3.py +706 -0
- multioptpy/ModelHessian/swartd4.py +918 -0
- multioptpy/ModelHessian/tshess.py +40 -0
- multioptpy/Optimizer/QHAdam.py +61 -0
- multioptpy/Optimizer/__init__.py +0 -0
- multioptpy/Optimizer/abc_fire.py +83 -0
- multioptpy/Optimizer/adabelief.py +58 -0
- multioptpy/Optimizer/adabound.py +68 -0
- multioptpy/Optimizer/adadelta.py +65 -0
- multioptpy/Optimizer/adaderivative.py +56 -0
- multioptpy/Optimizer/adadiff.py +68 -0
- multioptpy/Optimizer/adafactor.py +70 -0
- multioptpy/Optimizer/adam.py +65 -0
- multioptpy/Optimizer/adamax.py +62 -0
- multioptpy/Optimizer/adamod.py +83 -0
- multioptpy/Optimizer/adamw.py +65 -0
- multioptpy/Optimizer/adiis.py +523 -0
- multioptpy/Optimizer/afire_neb.py +282 -0
- multioptpy/Optimizer/block_hessian_update.py +709 -0
- multioptpy/Optimizer/c2diis.py +491 -0
- multioptpy/Optimizer/component_wise_scaling.py +405 -0
- multioptpy/Optimizer/conjugate_gradient.py +82 -0
- multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
- multioptpy/Optimizer/coordinate_locking.py +405 -0
- multioptpy/Optimizer/dic_rsirfo.py +1015 -0
- multioptpy/Optimizer/ediis.py +417 -0
- multioptpy/Optimizer/eve.py +76 -0
- multioptpy/Optimizer/fastadabelief.py +61 -0
- multioptpy/Optimizer/fire.py +77 -0
- multioptpy/Optimizer/fire2.py +249 -0
- multioptpy/Optimizer/fire_neb.py +92 -0
- multioptpy/Optimizer/gan_step.py +486 -0
- multioptpy/Optimizer/gdiis.py +609 -0
- multioptpy/Optimizer/gediis.py +203 -0
- multioptpy/Optimizer/geodesic_step.py +433 -0
- multioptpy/Optimizer/gpmin.py +633 -0
- multioptpy/Optimizer/gpr_step.py +364 -0
- multioptpy/Optimizer/gradientdescent.py +78 -0
- multioptpy/Optimizer/gradientdescent_neb.py +52 -0
- multioptpy/Optimizer/hessian_update.py +433 -0
- multioptpy/Optimizer/hybrid_rfo.py +998 -0
- multioptpy/Optimizer/kdiis.py +625 -0
- multioptpy/Optimizer/lars.py +21 -0
- multioptpy/Optimizer/lbfgs.py +253 -0
- multioptpy/Optimizer/lbfgs_neb.py +355 -0
- multioptpy/Optimizer/linesearch.py +236 -0
- multioptpy/Optimizer/lookahead.py +40 -0
- multioptpy/Optimizer/nadam.py +64 -0
- multioptpy/Optimizer/newton.py +200 -0
- multioptpy/Optimizer/prodigy.py +70 -0
- multioptpy/Optimizer/purtubation.py +16 -0
- multioptpy/Optimizer/quickmin_neb.py +245 -0
- multioptpy/Optimizer/radam.py +75 -0
- multioptpy/Optimizer/rfo_neb.py +302 -0
- multioptpy/Optimizer/ric_rfo.py +842 -0
- multioptpy/Optimizer/rl_step.py +627 -0
- multioptpy/Optimizer/rmspropgrave.py +65 -0
- multioptpy/Optimizer/rsirfo.py +1647 -0
- multioptpy/Optimizer/rsprfo.py +1056 -0
- multioptpy/Optimizer/sadam.py +60 -0
- multioptpy/Optimizer/samsgrad.py +63 -0
- multioptpy/Optimizer/tr_lbfgs.py +678 -0
- multioptpy/Optimizer/trim.py +273 -0
- multioptpy/Optimizer/trust_radius.py +207 -0
- multioptpy/Optimizer/trust_radius_neb.py +121 -0
- multioptpy/Optimizer/yogi.py +60 -0
- multioptpy/OtherMethod/__init__.py +0 -0
- multioptpy/OtherMethod/addf.py +1150 -0
- multioptpy/OtherMethod/dimer.py +895 -0
- multioptpy/OtherMethod/elastic_image_pair.py +629 -0
- multioptpy/OtherMethod/modelfunction.py +456 -0
- multioptpy/OtherMethod/newton_traj.py +454 -0
- multioptpy/OtherMethod/twopshs.py +1095 -0
- multioptpy/PESAnalyzer/__init__.py +0 -0
- multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
- multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
- multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
- multioptpy/PESAnalyzer/pca_analysis.py +314 -0
- multioptpy/Parameters/__init__.py +0 -0
- multioptpy/Parameters/atomic_mass.py +20 -0
- multioptpy/Parameters/atomic_number.py +22 -0
- multioptpy/Parameters/covalent_radii.py +44 -0
- multioptpy/Parameters/d2.py +61 -0
- multioptpy/Parameters/d3.py +63 -0
- multioptpy/Parameters/d4.py +103 -0
- multioptpy/Parameters/dreiding.py +34 -0
- multioptpy/Parameters/gfn0xtb_param.py +137 -0
- multioptpy/Parameters/gfnff_param.py +315 -0
- multioptpy/Parameters/gnb.py +104 -0
- multioptpy/Parameters/parameter.py +22 -0
- multioptpy/Parameters/uff.py +72 -0
- multioptpy/Parameters/unit_values.py +20 -0
- multioptpy/Potential/AFIR_potential.py +55 -0
- multioptpy/Potential/LJ_repulsive_potential.py +345 -0
- multioptpy/Potential/__init__.py +0 -0
- multioptpy/Potential/anharmonic_keep_potential.py +28 -0
- multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
- multioptpy/Potential/electrostatic_potential.py +69 -0
- multioptpy/Potential/flux_potential.py +30 -0
- multioptpy/Potential/gaussian_potential.py +101 -0
- multioptpy/Potential/idpp.py +516 -0
- multioptpy/Potential/keep_angle_potential.py +146 -0
- multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
- multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
- multioptpy/Potential/keep_potential.py +99 -0
- multioptpy/Potential/mechano_force_potential.py +74 -0
- multioptpy/Potential/nanoreactor_potential.py +52 -0
- multioptpy/Potential/potential.py +896 -0
- multioptpy/Potential/spacer_model_potential.py +221 -0
- multioptpy/Potential/switching_potential.py +258 -0
- multioptpy/Potential/universal_potential.py +34 -0
- multioptpy/Potential/value_range_potential.py +36 -0
- multioptpy/Potential/void_point_potential.py +25 -0
- multioptpy/SQM/__init__.py +0 -0
- multioptpy/SQM/sqm1/__init__.py +0 -0
- multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
- multioptpy/SQM/sqm2/__init__.py +0 -0
- multioptpy/SQM/sqm2/calc_tools.py +95 -0
- multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
- multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
- multioptpy/SQM/sqm2/sqm2_core.py +303 -0
- multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
- multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
- multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
- multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
- multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
- multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
- multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
- multioptpy/Thermo/__init__.py +0 -0
- multioptpy/Thermo/normal_mode_analyzer.py +865 -0
- multioptpy/Utils/__init__.py +0 -0
- multioptpy/Utils/bond_connectivity.py +264 -0
- multioptpy/Utils/calc_tools.py +884 -0
- multioptpy/Utils/oniom.py +96 -0
- multioptpy/Utils/pbc.py +48 -0
- multioptpy/Utils/riemann_curvature.py +208 -0
- multioptpy/Utils/symmetry_analyzer.py +482 -0
- multioptpy/Visualization/__init__.py +0 -0
- multioptpy/Visualization/visualization.py +156 -0
- multioptpy/WFAnalyzer/MO_analysis.py +104 -0
- multioptpy/WFAnalyzer/__init__.py +0 -0
- multioptpy/Wrapper/__init__.py +0 -0
- multioptpy/Wrapper/autots.py +1239 -0
- multioptpy/Wrapper/ieip_wrapper.py +93 -0
- multioptpy/Wrapper/md_wrapper.py +92 -0
- multioptpy/Wrapper/neb_wrapper.py +94 -0
- multioptpy/Wrapper/optimize_wrapper.py +76 -0
- multioptpy/__init__.py +5 -0
- multioptpy/entrypoints.py +916 -0
- multioptpy/fileio.py +660 -0
- multioptpy/ieip.py +340 -0
- multioptpy/interface.py +1086 -0
- multioptpy/irc.py +529 -0
- multioptpy/moleculardynamics.py +432 -0
- multioptpy/neb.py +1267 -0
- multioptpy/optimization.py +1553 -0
- multioptpy/optimizer.py +709 -0
- multioptpy-1.20.2.dist-info/METADATA +438 -0
- multioptpy-1.20.2.dist-info/RECORD +246 -0
- multioptpy-1.20.2.dist-info/WHEEL +5 -0
- multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
- multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
- multioptpy-1.20.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numpy.linalg import norm
|
|
3
|
+
|
|
4
|
+
from multioptpy.Parameters.parameter import UnitValueLib
|
|
5
|
+
|
|
6
|
+
class AFIRE_NEB:
|
|
7
|
+
"""Adaptive Fast Inertial Relaxation Engine for Nudged Elastic Band calculations.
|
|
8
|
+
|
|
9
|
+
This implementation extends the standard FIRE algorithm with adaptive parameters
|
|
10
|
+
for each image in the NEB chain, improving convergence for reaction paths that have
|
|
11
|
+
regions with different curvatures and energy barriers.
|
|
12
|
+
|
|
13
|
+
Reference:
|
|
14
|
+
E. Bitzek et al., Phys. Rev. Lett. 97, 170201 (2006)
|
|
15
|
+
with adaptive extensions for NEB.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, **config):
|
|
19
|
+
# Configuration parameters
|
|
20
|
+
self.config = config
|
|
21
|
+
|
|
22
|
+
# Initialize flags
|
|
23
|
+
self.Initialization = True
|
|
24
|
+
self.iter = 0
|
|
25
|
+
|
|
26
|
+
# FIRE parameters
|
|
27
|
+
self.a_start = config.get("a_start", 0.1)
|
|
28
|
+
self.f_inc = config.get("f_inc", 1.1)
|
|
29
|
+
self.f_dec = config.get("f_dec", 0.5)
|
|
30
|
+
self.f_a = config.get("f_a", 0.99)
|
|
31
|
+
self.dt_start = config.get("dt_start", 0.1)
|
|
32
|
+
self.dt_max = config.get("dt_max", 1.0)
|
|
33
|
+
self.n_min = config.get("n_min", 5)
|
|
34
|
+
|
|
35
|
+
# Per-image adaptive parameters
|
|
36
|
+
self.a_images = None # α values for each image
|
|
37
|
+
self.dt_images = None # Time steps for each image
|
|
38
|
+
self.n_pos_images = None # Consecutive positive dot products counter for each image
|
|
39
|
+
|
|
40
|
+
# Storage for velocities and previous forces
|
|
41
|
+
self.velocities = None
|
|
42
|
+
self.prev_forces = None
|
|
43
|
+
self.prev_positions = None
|
|
44
|
+
|
|
45
|
+
# Maximum step size constraint
|
|
46
|
+
self.maxstep = config.get("maxstep", 0.1)
|
|
47
|
+
|
|
48
|
+
# Unit conversion
|
|
49
|
+
self.bohr2angstroms = UnitValueLib().bohr2angstroms
|
|
50
|
+
if 'bohr2angstroms' in config:
|
|
51
|
+
self.bohr2angstroms = config['bohr2angstroms']
|
|
52
|
+
|
|
53
|
+
# For compatibility with other NEB implementations
|
|
54
|
+
self.TR_NEB = config.get("TR_NEB", None)
|
|
55
|
+
|
|
56
|
+
print(f"Initialized Adaptive FIRE optimizer for NEB with "
|
|
57
|
+
f"a_start={self.a_start}, dt_start={self.dt_start}, maxstep={self.maxstep}")
|
|
58
|
+
|
|
59
|
+
def initialize_data(self, num_images):
|
|
60
|
+
"""Initialize data structures for the optimization.
|
|
61
|
+
|
|
62
|
+
Parameters:
|
|
63
|
+
----------
|
|
64
|
+
num_images : int
|
|
65
|
+
Number of images in the NEB calculation
|
|
66
|
+
"""
|
|
67
|
+
# Initialize per-image parameters
|
|
68
|
+
self.a_images = np.ones(num_images) * self.a_start
|
|
69
|
+
self.dt_images = np.ones(num_images) * self.dt_start
|
|
70
|
+
self.n_pos_images = np.zeros(num_images, dtype=int)
|
|
71
|
+
|
|
72
|
+
# Initialize velocities to zero with correct shapes
|
|
73
|
+
self.velocities = [np.zeros_like(img) for img in range(num_images)]
|
|
74
|
+
|
|
75
|
+
# Initialize storage for previous values
|
|
76
|
+
self.prev_forces = [None] * num_images
|
|
77
|
+
self.prev_positions = [None] * num_images
|
|
78
|
+
|
|
79
|
+
print(f"Initialized AFIRE parameters for {num_images} images")
|
|
80
|
+
|
|
81
|
+
def update_velocities_and_positions(self, positions, forces, dt):
|
|
82
|
+
"""Update velocities and positions using the FIRE algorithm for multiple images.
|
|
83
|
+
|
|
84
|
+
Parameters:
|
|
85
|
+
----------
|
|
86
|
+
positions : list of ndarray
|
|
87
|
+
Current positions for all images
|
|
88
|
+
forces : list of ndarray
|
|
89
|
+
Current forces for all images
|
|
90
|
+
dt : ndarray
|
|
91
|
+
Time steps for each image
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
-------
|
|
95
|
+
tuple
|
|
96
|
+
(new_velocities, steps) - Updated velocities and position steps for all images
|
|
97
|
+
"""
|
|
98
|
+
new_velocities = []
|
|
99
|
+
steps = []
|
|
100
|
+
|
|
101
|
+
# Process each image independently
|
|
102
|
+
for i in range(len(positions)):
|
|
103
|
+
# Get current force and position
|
|
104
|
+
force = forces[i]
|
|
105
|
+
position = positions[i]
|
|
106
|
+
|
|
107
|
+
# Initialize velocity if needed
|
|
108
|
+
if self.velocities[i] is None or not isinstance(self.velocities[i], np.ndarray):
|
|
109
|
+
self.velocities[i] = np.zeros_like(position)
|
|
110
|
+
|
|
111
|
+
velocity = self.velocities[i]
|
|
112
|
+
|
|
113
|
+
# Ensure velocity has the same shape as force
|
|
114
|
+
if velocity.shape != force.shape:
|
|
115
|
+
velocity = np.zeros_like(force)
|
|
116
|
+
print(f"Warning: Resetting velocity for image {i} due to shape mismatch")
|
|
117
|
+
|
|
118
|
+
# Calculate power (P = F·v)
|
|
119
|
+
power = np.dot(force.flatten(), velocity.flatten())
|
|
120
|
+
|
|
121
|
+
# Calculate velocity magnitude and force magnitude
|
|
122
|
+
v_norm = norm(velocity.flatten())
|
|
123
|
+
f_norm = norm(force.flatten())
|
|
124
|
+
|
|
125
|
+
# Update velocity using FIRE algorithm
|
|
126
|
+
if f_norm > 0 and v_norm > 0:
|
|
127
|
+
# Calculate normalized force and velocity vectors
|
|
128
|
+
f_hat = force.flatten() / f_norm
|
|
129
|
+
v_hat = velocity.flatten() / v_norm
|
|
130
|
+
|
|
131
|
+
# Mix velocity and force direction according to α
|
|
132
|
+
mixed_v = (1.0 - self.a_images[i]) * velocity + self.a_images[i] * v_norm * f_hat.reshape(velocity.shape)
|
|
133
|
+
else:
|
|
134
|
+
mixed_v = velocity.copy()
|
|
135
|
+
|
|
136
|
+
# Perform velocity Verlet integration
|
|
137
|
+
new_v = mixed_v + 0.5 * dt[i] * force
|
|
138
|
+
|
|
139
|
+
# Calculate step
|
|
140
|
+
step = dt[i] * new_v
|
|
141
|
+
|
|
142
|
+
# Apply max step constraint
|
|
143
|
+
step_length = norm(step.flatten())
|
|
144
|
+
if step_length > self.maxstep:
|
|
145
|
+
step = step * (self.maxstep / step_length)
|
|
146
|
+
|
|
147
|
+
# Store results
|
|
148
|
+
new_velocities.append(new_v)
|
|
149
|
+
steps.append(step)
|
|
150
|
+
|
|
151
|
+
# Update FIRE parameters based on power
|
|
152
|
+
if power > 0: # Moving in right direction
|
|
153
|
+
self.n_pos_images[i] += 1
|
|
154
|
+
if self.n_pos_images[i] > self.n_min:
|
|
155
|
+
# Increase time step and decrease mixing parameter
|
|
156
|
+
self.dt_images[i] = min(self.dt_images[i] * self.f_inc, self.dt_max)
|
|
157
|
+
self.a_images[i] *= self.f_a
|
|
158
|
+
else: # Moving in wrong direction
|
|
159
|
+
# Reset parameters
|
|
160
|
+
self.n_pos_images[i] = 0
|
|
161
|
+
self.dt_images[i] *= self.f_dec
|
|
162
|
+
self.a_images[i] = self.a_start
|
|
163
|
+
# Reset velocity (stop inertial motion against force)
|
|
164
|
+
new_velocities[i] = np.zeros_like(velocity)
|
|
165
|
+
|
|
166
|
+
return new_velocities, steps
|
|
167
|
+
|
|
168
|
+
def determine_step(self, dr):
|
|
169
|
+
"""Ensure step sizes are within maxstep.
|
|
170
|
+
|
|
171
|
+
Parameters:
|
|
172
|
+
----------
|
|
173
|
+
dr : list of ndarray
|
|
174
|
+
Step vectors for all images
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
-------
|
|
178
|
+
list of ndarray
|
|
179
|
+
Step vectors constrained by maxstep if necessary
|
|
180
|
+
"""
|
|
181
|
+
if self.maxstep is None:
|
|
182
|
+
return dr
|
|
183
|
+
|
|
184
|
+
# Handle list of step vectors
|
|
185
|
+
result = []
|
|
186
|
+
for step in dr:
|
|
187
|
+
# Calculate step length
|
|
188
|
+
step_reshaped = step.reshape(-1, 3) if step.size % 3 == 0 else step
|
|
189
|
+
steplengths = np.sqrt((step_reshaped**2).sum(axis=1))
|
|
190
|
+
longest_step = np.max(steplengths)
|
|
191
|
+
|
|
192
|
+
# Scale step if necessary
|
|
193
|
+
if longest_step > self.maxstep:
|
|
194
|
+
result.append(step * (self.maxstep / longest_step))
|
|
195
|
+
else:
|
|
196
|
+
result.append(step)
|
|
197
|
+
return result
|
|
198
|
+
|
|
199
|
+
def AFIRE_NEB_calc(self, geometry_num_list, total_force_list, pre_total_velocity,
|
|
200
|
+
optimize_num, total_velocity, cos_list,
|
|
201
|
+
biased_energy_list, pre_biased_energy_list, pre_geom):
|
|
202
|
+
"""Calculate step using Adaptive FIRE for NEB.
|
|
203
|
+
|
|
204
|
+
Parameters:
|
|
205
|
+
----------
|
|
206
|
+
geometry_num_list : ndarray
|
|
207
|
+
Current geometry coordinates for all images
|
|
208
|
+
total_force_list : ndarray
|
|
209
|
+
Current forces for all images
|
|
210
|
+
pre_total_velocity : ndarray
|
|
211
|
+
Previous velocities for all images
|
|
212
|
+
optimize_num : int
|
|
213
|
+
Current optimization iteration number
|
|
214
|
+
total_velocity : ndarray
|
|
215
|
+
Current velocities for all images
|
|
216
|
+
cos_list : ndarray
|
|
217
|
+
Cosines between adjacent images
|
|
218
|
+
biased_energy_list : ndarray
|
|
219
|
+
Current energy for all images
|
|
220
|
+
pre_biased_energy_list : ndarray
|
|
221
|
+
Previous energy for all images
|
|
222
|
+
pre_geom : ndarray
|
|
223
|
+
Previous geometry coordinates for all images
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
-------
|
|
227
|
+
ndarray
|
|
228
|
+
Updated geometry coordinates for all images
|
|
229
|
+
"""
|
|
230
|
+
print(f"\n{'='*50}\nNEB-AFIRE Iteration {self.iter}\n{'='*50}")
|
|
231
|
+
|
|
232
|
+
# Get number of images
|
|
233
|
+
num_images = len(geometry_num_list)
|
|
234
|
+
|
|
235
|
+
# Initialize data structures if first iteration
|
|
236
|
+
if self.Initialization:
|
|
237
|
+
self.initialize_data(num_images)
|
|
238
|
+
self.Initialization = False
|
|
239
|
+
print("First iteration - initializing AFIRE")
|
|
240
|
+
|
|
241
|
+
# Initialize velocities from pre_total_velocity if available
|
|
242
|
+
if pre_total_velocity is not None and len(pre_total_velocity) >= num_images:
|
|
243
|
+
self.velocities = [vel.copy() if vel is not None else np.zeros_like(geometry_num_list[i])
|
|
244
|
+
for i, vel in enumerate(pre_total_velocity[:num_images])]
|
|
245
|
+
else:
|
|
246
|
+
self.velocities = [np.zeros_like(geom) for geom in geometry_num_list]
|
|
247
|
+
|
|
248
|
+
# Track adaptive parameters
|
|
249
|
+
min_dt = np.min(self.dt_images)
|
|
250
|
+
max_dt = np.max(self.dt_images)
|
|
251
|
+
avg_a = np.mean(self.a_images)
|
|
252
|
+
print(f"FIRE parameters - dt range: [{min_dt:.4f}, {max_dt:.4f}], avg α: {avg_a:.4f}")
|
|
253
|
+
|
|
254
|
+
# Update velocities and calculate steps
|
|
255
|
+
new_velocities, move_vectors = self.update_velocities_and_positions(
|
|
256
|
+
geometry_num_list, total_force_list, self.dt_images
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Apply trust region correction if TR_NEB is provided (for compatibility)
|
|
260
|
+
if self.TR_NEB:
|
|
261
|
+
move_vectors = self.TR_NEB.TR_calc(geometry_num_list, total_force_list, move_vectors,
|
|
262
|
+
biased_energy_list, pre_biased_energy_list, pre_geom)
|
|
263
|
+
|
|
264
|
+
# Store updated velocities
|
|
265
|
+
self.velocities = new_velocities
|
|
266
|
+
|
|
267
|
+
# Update geometry using move vectors
|
|
268
|
+
new_geometry = []
|
|
269
|
+
for i, (geom, move) in enumerate(zip(geometry_num_list, move_vectors)):
|
|
270
|
+
new_geom = geom + move
|
|
271
|
+
new_geometry.append(new_geom)
|
|
272
|
+
|
|
273
|
+
# Store current force and position for next iteration
|
|
274
|
+
self.prev_forces[i] = total_force_list[i].copy()
|
|
275
|
+
self.prev_positions[i] = geometry_num_list[i].copy()
|
|
276
|
+
|
|
277
|
+
# Convert to numpy array and apply unit conversion
|
|
278
|
+
new_geometry = np.array(new_geometry)
|
|
279
|
+
new_geometry *= self.bohr2angstroms
|
|
280
|
+
|
|
281
|
+
self.iter += 1
|
|
282
|
+
return new_geometry
|