def example_1(): # In this example, we will generate a smooth CNH-HCN isomerization # guessed pathway # Step 1 - Generate the bad initial guess print("Step 1 - Generate the bad initial guess...") H_coords = [(2, 0), (2, 1), (1, 1), (0, 1), (-1, 1), (-1, 0)] CNH_frames = [[ structures.Atom("C", 0, 0, 0), structures.Atom("N", 1, 0, 0), structures.Atom("H", x, y, 0) ] for x, y in H_coords] # Further, randomly rotate the atoms CNH_frames = [ geometry.perturbate(frame, dx=0.0, dr=360) for frame in CNH_frames ] files.write_xyz(CNH_frames, "rotated_pathway.xyz") # Step 2 - Use procrustes to remove rotations print("Step 2 - Use Procrustes to remove rotations...") geometry.procrustes(CNH_frames) files.write_xyz(CNH_frames, "procrustes_pathway.xyz") # Step 3 - Smooth out the band by minimizing the RMS atomic motion between # consecutive frames until it is below 0.1 (with a max of 50 frames). print("Step 3 - Smooth out the band...") CNH_frames = geometry.smooth_xyz(CNH_frames, R_max=0.1, F_max=50, use_procrustes=True) # Save smoothed band files.write_xyz(CNH_frames, "smoothed_pathway.xyz")
rmax = float(argv[argv.index('-rmax') + 1]) if "-fmax" in argv[2:]: fmax = int(argv[argv.index('-fmax') + 1]) frames = files.read_xyz(path + file_name + ".xyz") if interpolate: frames_hold = [copy.deepcopy(f) for f in frames] if b_start is not None: frames = frames[b_start:b_stop] frames = geometry.smooth_xyz(frames, R_MAX=rmax, F_MAX=fmax, PROCRUSTES=True, N_FRAMES=nframes) if b_start is not None: a = frames_hold[:b_start] b = frames c = frames_hold[b_stop - 1:] if not isinstance(a[0], list): a = [a] if not isinstance(b[0], list): b = [b] if not isinstance(c[0], list): c = [c] frames = a + b + c _ = geometry.procrustes(frames) files.write_xyz(frames, path + file_name + append + ".xyz")
# First we want to read in the manually made iterations fptrs = [int(f.split(".xyz")[0]) for f in os.listdir("reaction_coordinate")] fptrs.sort() # Now, we loop through all files in numerical order and append to our reaction coordinate rxn = [] for f in fptrs: rxn.append(files.read_xyz("reaction_coordinate/%d.xyz" % f)) # Save an example of this rough reaction we made files.write_xyz(rxn, "reaction_coordinate_rough") # Now, we smooth it out. There are many ways of doing so. We'll only show the main two methods here # Here we just make a copy of the frames for the second method held_rough_reaction = copy.deepcopy(rxn) # Method 1 - Procrustes to minimize rotations and translations between consecutive frames geometry.procrustes(rxn) files.write_xyz(rxn, "reaction_coordinate_procrustes") # Method 2 - Procrustes plus linear interpolation # Note, R_MAX is the maximum average change in atomic positions between adjacent frames (in angstroms) # F_MAX is the maximum number of frames we want in the final reaction coordinate rxn = copy.deepcopy(held_rough_reaction) # Grab the previously rough reaction geometry.smooth_xyz(rxn, R_MAX=0.1, F_MAX=50, PROCRUSTES=True, outName="reaction_coordinate_smooth", write_xyz=True)
def procrustes(): # Default Documentation help_info = ''' procrustes --------- A command line tool to run procrustes along an xyz file. procrustes [file.xyz] [Options] Flag Default Description -help, -h : : Print this help menu -overwrite, -o : : Overwrite the initial file -append, -a : _proc : Change the appended name alteration -interpolate, -i : : This will turn on linear interpolation -rmax : 0.5 : The default max rms for interpolation -fmax : 25 : The default max number of frames for interpolation -nframes, -n : : If specified, interpolate to exactly n frames. -between, -b : : If specified, then interpolation is only run between the two frames. Note, this is [x, y) inclusive. Default behaviour is to use procrustes on an xyz to best align the coordinates, and then to save a new xyz file with the name OLD_proc.xyz (where OLD is the original xyz file name). NOTE! If you specify -o and -a, then appending will occur instead of overwritting. Ex. procrustes demo.xyz procrustes demo.xyz -i -n 20 procrustes demo.xyz -i -rmax 0.1 -fmax 30 procrustes demo.xyz -i -b 5 8 -n 6 ''' # Parse Arguments if '-h' in argv or '-help' in argv or len(argv) < 2: print(help_info) exit() # Parse Arguments append = "_proc" path = os.getcwd() if not path.endswith("/"): path += "/" file_name = argv[1] interpolate = False rmax = 0.5 fmax = 25 nframes = None b_start, b_stop = None, None if ".xyz" in file_name: file_name = file_name.split(".xyz")[0] if "-o" in argv[2:]: append = "" elif "-overwrite" in argv[2:]: append = "" if "-i" in argv[2:]: interpolate = True elif "-interpolate" in argv[2:]: interpolate = True if "-a" in argv[2:]: append = argv[argv.index('-a') + 1] elif "-append" in argv[2:]: append = argv[argv.index('-append') + 1] if "-n" in argv[2:]: nframes = int(argv[argv.index('-n') + 1]) elif "-nframes" in argv[2:]: nframes = int(argv[argv.index('-nframes') + 1]) if "-b" in argv[2:]: b_start = int(argv[argv.index('-b') + 1]) b_stop = int(argv[argv.index('-b') + 2]) elif "-between" in argv[2:]: b_start = int(argv[argv.index('-between') + 1]) b_stop = int(argv[argv.index('-between') + 2]) assert b_start >= 0, "b_start must be >= 0." assert b_stop >= 2 + b_start, "b_stop must be >= 2 + b_start." if "-rmax" in argv[2:]: rmax = float(argv[argv.index('-rmax') + 1]) if "-fmax" in argv[2:]: fmax = int(argv[argv.index('-fmax') + 1]) frames = files.read_xyz(path + file_name + ".xyz") if interpolate: frames_hold = [copy.deepcopy(f) for f in frames] if b_start is not None: frames = frames[b_start:b_stop] frames = geometry.smooth_xyz(frames, R_max=rmax, F_max=fmax, N_frames=nframes, use_procrustes=True) if b_start is not None: a = frames_hold[:b_start] b = frames c = frames_hold[b_stop - 1:] if not isinstance(a[0], list): a = [a] if not isinstance(b[0], list): b = [b] if not isinstance(c[0], list): c = [c] frames = a + b + c _ = geometry.procrustes(frames) files.write_xyz(frames, path + file_name + append + ".xyz")
def align_coordinates(self, r, B=None, H=None, return_matrix=False): ''' Get a rotation matrix A that will remove rigid rotation from the new coordinates r. Further, if another vector needs rotating by the same matrix A, it should be passed in B and will be rotated. If a matrix also needs rotating, it can be passed as H and also be rotated. *Parameters* r: *list, float* 1D array of atomic coordinates to be rotated by procrustes matrix A. B: *list, list, float, optional* A list of vectors that may also be rotated by the same matrix as *r*. H: *list, list, float, optional* A matrix that should also be rotated via: H = R * H * R.T return_matrix: *bool, optional* Whether to also return the rotation matrix used or not. *Returns* rotations: *dict* A dictionary holding 'A', the rotation matrix, 'r', the rotated new coordinates, 'B', a list of all other vectors that were rotated, and 'H', a rotated matrix. ''' # Prevent rotation or translation coord_count = 0 st = self.states for s in st[1:-1]: for a in s: a.x, a.y, a.z = r[coord_count:coord_count + 3] coord_count += 3 # Translate and rotate each frame to fit its neighbor # Note, procrustes will change st[-1] which is fine as we need this # for spring force calculations A = geometry.procrustes(st) coord_count = 0 for s in st[1:-1]: for a in s: r[coord_count:coord_count + 3] = [a.x, a.y, a.z] coord_count += 3 C = [] R = block_diag(*A[0:-len(st[0])]) if B is not None: for b in B: # Validation code that a 1D array here works the same way. # shape = b.shape # b2 = b.flatten().dot(R) # C.append(b2.reshape(shape)) C.append(np.dot(b, R)) if C == []: C = None if H is not None: # Note, to transform the Hessian matrix, it's not like a normal # vector (as above) H = R * H * R.T return_this = {'A': A, 'r': r, 'B': C, 'H': H} return return_this
def __init__(self, name, states, theory, extra_section='', initial_guess=None, spring_atoms=None, nprocs=1, queue=None, mem=2000, priority=None, disp=0, k=0.00367453, # 0.1 eV/A in Ha/A charge=0, multiplicity=1, fit_rigid=True, DFT='orca', opt='LBFGS', start_job=None, get_results=None, new_opt_params={}, callback=None, ci_neb=False, ci_N=5, no_energy=False): self.name = name self.states = states self.theory = theory self.extra_section = extra_section self.initial_guess = initial_guess self.spring_atoms = spring_atoms self.nprocs = nprocs self.queue = queue self.mem = mem self.priority = priority self.disp = disp self.k = k self.charge = charge self.multiplicity = multiplicity self.DFT = DFT self.opt = opt.lower() self.fit_rigid = fit_rigid self.start_job = start_job self.get_results = get_results self.ci_neb = ci_neb self.ci_img = None self.ci_N = ci_N # These are variables to make life easier on the user after NEB runs self.last_iteration_run = None self.highest_energy_frame_index = None self.no_energy = no_energy self.new_opt_params = new_opt_params if 'fit_rigid' not in new_opt_params: new_opt_params['fit_rigid'] = fit_rigid # Other starting parameters self.prv_RMS = None self.prv_MAX = None self.prv_MAX_E = None self.nframes = len(states) self.RMS_force = float('inf') self.MAX_force = float('inf') self.MAX_energy = float('inf') self.step = 0 self.calls_to_force = 0 self.calls_to_calculate = 0 self.callback = callback self.job_hang_time = None self.DFT = DFT.lower().strip() if self.DFT == 'orca': self.start_job = orca_start_job self.get_results = orca_results if self.DFT == 'g09': self.start_job = g09_start_job self.get_results = g09_results if (self.start_job is None or self.get_results is None): raise Exception("Error - You need to either specify DFT as orca or \ g09. If not, you need to manually specify start_job and get_results.") # In all cases, optimize the path via rigid rotations first # Set to only if fit_rigid for comparison purposes if self.fit_rigid: geometry.procrustes(self.states) # Load initial coordinates into flat array for optimizer self.coords_start = [] for s in states[1:-1]: for a in s: self.coords_start += [a.x, a.y, a.z] # Raise warnings if odd starting parameters used if self.ci_neb and "linesearch" in self.new_opt_params and self.new_opt_params["linesearch"] is not None: print("\nWARNING\nYou have chosen to have climbing image with a linesearch.") print("It is recommended to set linesearch to None, as at times this prevents the climbing image to") print("increase the energy as needed.") elif self.ci_neb and "linesearch" not in self.new_opt_params and self.opt in ["BFGS", "LBFGS", "CG", "SD"]: print("\nWARNING\nYou have chosen to have climbing image. Beware that the default parameters for the") print("optimizer you chose may have a linesearch set. It is recommended to set linesearch to None,") print("as at times this prevents the climbing image to increase the energy as needed.") if self.ci_neb and self.no_energy: raise Exception("\nERROR\nUnable to do climbing image without energy. Let no_energy be False.")
def __init__(self, name, states, theory, extra_section='', charge=0, initial_guess=None, spring_atoms=None, procs=1, queue=None, mem=2000, priority=None, disp=0, k_max=0.1837, gamma=0.2, fit_rigid=True, DFT='orca', opt='LBFGS', start_job=None, get_results=None, new_opt_params={}, callback=None): self.name = name self.states = states self.theory = theory self.extra_section = extra_section self.charge = charge self.initial_guess = initial_guess self.spring_atoms = spring_atoms self.procs = procs self.queue = queue self.mem = mem self.priority = priority self.disp = disp self.peak = 0 self.k_max = k_max self.k = [] self.gamma = gamma self.DFT = DFT self.opt = opt.lower() self.fit_rigid = fit_rigid self.start_job = start_job self.get_results = get_results self.new_opt_params = new_opt_params if 'fit_rigid' not in new_opt_params: new_opt_params['fit_rigid'] = fit_rigid # Other starting parameters self.prv_RMS = None self.prv_MAX = None self.prv_MAX_E = None self.nframes = len(states) self.RMS_force = float('inf') self.MAX_force = float('inf') self.MAX_energy = float('inf') self.step = 0 self.initialize = True self.calls_to_calculate = 0 self.callback = callback self.DFT = DFT.lower().strip() if self.DFT == 'orca': self.start_job = orca_start_job self.get_results = orca_results if self.DFT == 'g09': self.start_job = g09_start_job self.get_results = g09_results if (self.start_job is None or self.get_results is None): raise Exception("Error - You need to either specify DFT as orca or \ g09. If not, you need to manually specify start_job and get_results.") # In all cases, optimize the path via rigid rotations first # Set to only if fit_rigid for comparison purposes if self.fit_rigid: geometry.procrustes(self.states) # Load initial coordinates into flat array for optimizer self.coords_start = [] for s in states[1:-1]: for a in s: self.coords_start += [a.x, a.y, a.z]
def __init__( self, name, states, theory, extra_section='', initial_guess=None, spring_atoms=None, procs=1, queue=None, mem=2000, priority=None, disp=0, k=0.00367453, # 0.1 eV/A in Ha/A charge=0, fit_rigid=True, DFT='orca', opt='LBFGS', start_job=None, get_results=None, new_opt_params={}, new_auto_opt_params={}, callback=None, ci_ANEB=False, ci_N=5, ANEB_Nsim=5, ANEB_Nmax=15, add_by_energy=False): self.name = name self.states = states self.all_states = copy.deepcopy(self.states) self.theory = theory self.extra_section = extra_section self.initial_guess = initial_guess self.spring_atoms = spring_atoms self.procs = procs self.queue = queue self.mem = mem self.priority = priority self.disp = disp self.k = [k for i in states[:-1]] self.k_all = copy.deepcopy(self.k) self.k_0 = k self.charge = charge self.DFT = DFT self.opt = opt.lower() self.fit_rigid = fit_rigid self.start_job = start_job self.get_results = get_results self.ci_ANEB = ci_ANEB self.ci_img = None self.ci_N = ci_N self.add_by_energy = add_by_energy self.ANEB_Nsim = ANEB_Nsim self.ANEB_Nmax = ANEB_Nmax self.energy_gaps = None self.debug = False self.new_opt_params = new_opt_params if 'fit_rigid' not in new_opt_params: new_opt_params['fit_rigid'] = fit_rigid self.new_auto_opt_params = new_auto_opt_params if 'fit_rigid' not in new_auto_opt_params: new_auto_opt_params['fit_rigid'] = fit_rigid # Other starting parameters self.prv_RMS = None self.prv_MAX = None self.prv_MAX_E = None self.nframes = len(states) self.RMS_force = float('inf') self.MAX_force = float('inf') self.MAX_energy = float('inf') self.step = 0 self.calls_to_calculate = 0 self.calls_to_force = 0 self.callback = callback self.DFT = DFT.lower().strip() if self.DFT == 'orca': self.start_job = orca_start_job self.get_results = orca_results if self.DFT == 'g09': self.start_job = g09_start_job self.get_results = g09_results if (self.start_job is None or self.get_results is None): raise Exception( "Error - You need to either specify DFT as orca or \ g09. If not, you need to manually specify start_job and get_results.") # Ensure we have ANEB_Nsim at minimum while len(self.states) < self.ANEB_Nsim: add_frame(self) if self.fit_rigid: geometry.procrustes(self.states) # In all cases, optimize the path via rigid rotations first # Set to only if fit_rigid for comparison purposes if self.fit_rigid: geometry.procrustes(self.states) self.all_states = copy.deepcopy(self.states) # Load initial coordinates into flat array for optimizer self.flattened_states = flattened(self.states) # Raise warnings if odd starting parameters used if self.ci_ANEB and "linesearch" in self.new_opt_params and self.new_opt_params[ "linesearch"] is not None: print( "\nWARNING\nYou have chosen to have climbing image with a linesearch." ) print( "It is recommended to set linesearch to None, as at times this prevents the climbing image to" ) print("increase the energy as needed.") elif self.ci_ANEB and "linesearch" not in self.new_opt_params and self.opt in [ "BFGS", "LBFGS", "CG", "SD" ]: print( "\nWARNING\nYou have chosen to have climbing image. Beware that the default parameters for the" ) print( "optimizer you chose may have a linesearch set. It is recommended to set linesearch to None," ) print( "as at times this prevents the climbing image to increase the energy as needed." )