def propagate_bundle(master, dt): """Propagates the Bundle object with RK4.""" ncrd = master.traj[0].dim ntraj = master.n_traj() kx = np.zeros((ntraj, rk_ordr, ncrd)) kp = np.zeros((ntraj, rk_ordr, ncrd)) kg = np.zeros((ntraj, rk_ordr, ncrd)) for rk in range(rk_ordr): tmpbundle = master.copy() for i in range(ntraj): if tmpbundle.traj[i].active: propagate_rk(tmpbundle.traj[i], dt, rk, kx[i], kp[i], kg[i]) # update the PES to evaluate new gradients if rk < rk_ordr - 1: surface.update_pes(tmpbundle, update_centroids=False) # update to the final position for i in range(ntraj): if master.traj[i].active: master.traj[i].update_x(master.traj[i].x() + np.sum(wgt[:,np.newaxis]*kx[i], axis=0)) master.traj[i].update_p(master.traj[i].p() + np.sum(wgt[:,np.newaxis]*kp[i], axis=0)) if propphase: master.traj[i].update_phase(master.traj[i].phase() + np.sum(wgt[:,np.newaxis]*kg[i], axis=0)) surface.update_pes(master)
def propagate_bundle(master, dt): """Propagates the Bundle object with VV.""" # update position for i in range(master.n_traj()): if master.traj[i].active: propagate_position(master.traj[i], dt) # update electronic structure for all trajectories # and centroids (where necessary) surface.update_pes(master) # finish update of momentum and phase for i in range(master.n_traj()): if master.traj[i].active: propagate_momentum(master.traj[i], dt)
def init_bundle(master): """Initializes the trajectories.""" # initialize the interface we'll be using the determine the # the PES. There are some details here that trajectories # will want to know about glbl.pes.init_interface() # now load the initial trajectories into the bundle if glbl.sampling['restart']: init_restart(master) else: # first generate the initial nuclear coordinates and momenta # and add the resulting trajectories to the bundle glbl.distrib.set_initial_coords(master) # set the initial state of the trajectories in bundle. This may # require evaluation of electronic structure set_initial_state(master) # set the initial amplitudes of the basis functions set_initial_amplitudes(master) # add virtual basis functions, if desired (i.e. virtual basis = true) if glbl.sampling['virtual_basis'] and not glbl.sampling['restart']: virtual_basis(master) # update all pes info for all trajectories and centroids (where necessary) surface.update_pes(master) # compute the hamiltonian matrix... master.update_matrices() # so that we may appropriately renormalize to unity master.renormalize() # this is the bundle at time t=0. Save in order to compute auto # correlation function set_initial_bundle(master) # write to the log files if glbl.mpi['rank'] == 0: master.update_logs() fileio.print_fms_logfile( 't_step', [master.time, glbl.propagate['default_time_step'], master.nalive]) return master.time
def set_initial_amplitudes(master): """Sets the initial amplitudes.""" # if init_amp_overlap is set, overwrite 'amplitudes' that was # set in fms.input if glbl.nuclear_basis['init_amp_overlap']: origin = make_origin_traj() # update all pes info for all trajectories and centroids (where necessary) if glbl.integrals.overlap_requires_pes: surface.update_pes(master) # Calculate the initial expansion coefficients via projection onto # the initial wavefunction that we are sampling ovec = np.zeros(master.n_traj(), dtype=complex) for i in range(master.n_traj()): ovec[i] = glbl.integrals.traj_overlap(master.traj[i], origin, nuc_only=True) smat = np.zeros((master.n_traj(), master.n_traj()), dtype=complex) for i in range(master.n_traj()): for j in range(i + 1): smat[i, j] = glbl.integrals.traj_overlap(master.traj[i], master.traj[j]) if i != j: smat[j, i] = smat[i, j].conjugate() sinv = sp_linalg.pinvh(smat) glbl.nuclear_basis['amplitudes'] = np.dot(sinv, ovec) # if we don't have a sufficient number of amplitudes, append # amplitudes with "zeros" as necesary if len(glbl.nuclear_basis['amplitudes']) < master.n_traj(): dif = master.n_traj() - len(glbl.nuclear_basis['amplitudes']) fileio.print_fms_logfile( 'warning', ['appending ' + str(dif) + ' values of 0+0j to amplitudes']) glbl.nuclear_basis['amplitudes'].extend([0 + 0j for i in range(dif)]) # finally -- update amplitudes in the bundle for i in range(master.n_traj()): master.traj[i].update_amplitude(glbl.nuclear_basis['amplitudes'][i]) return
def propagate_bundle(master, dt): """Propagates the Bundle object with BS.""" global h ncrd = master.traj[0].dim ntraj = master.n_traj() t = 0. if h is None: h = dt while abs(t) < abs(dt): hstep = np.sign(dt) * min(abs(h), abs(dt - t)) reduced = False tsav = np.zeros(kmax) err = np.zeros(kmax) Tx = np.zeros((kmax, ntraj, ncrd)) Tp = np.zeros((kmax, ntraj, ncrd)) Tg = np.zeros((kmax, ntraj, ncrd)) for k in range(kmax): tmpbundle = master.copy() x0 = np.zeros((ntraj, ncrd)) p0 = np.zeros((ntraj, ncrd)) g0 = np.zeros((ntraj, ncrd)) x1 = np.zeros((ntraj, ncrd)) p1 = np.zeros((ntraj, ncrd)) g1 = np.zeros((ntraj, ncrd)) # step through n modified midpoint steps for n in range(nstep[k]): for i in range(ntraj): if tmpbundle.traj[i].active: mm_step(tmpbundle.traj[i], hstep/nstep[k], x0[i], x1[i], p0[i], p1[i], g0[i], g1[i], n) surface.update_pes(tmpbundle, update_centroids=False) # compute the modified midpoint estimate for i in range(ntraj): if master.traj[i].active: x1[i] = 0.5*(x1[i] + x0[i] + hstep/nstep[k]*tmpbundle.traj[i].velocity()) p1[i] = 0.5*(p1[i] + p0[i] + hstep/nstep[k]*tmpbundle.traj[i].force()) if propphase: g1[i] = 0.5*(g1[i] + g0[i] + hstep/nstep[k]*tmpbundle.traj[i].phase_dot()) # extrapolate from modified midpoint results poly_extrapolate(k, (hstep/nstep[k])**2, tsav, x1, p1, g1, Tx, Tp, Tg) if k > 0: errmax = np.amax((abs(Tx[k]), abs(Tp[k]), abs(Tg[k]))) / tol err[k-1] = (errmax / 0.25) ** (1. / (2.*k + 1.)) if k >= kopt - 2: if errmax > 1: # scale the time step and try again sfac, reduced = reduce_tstep(k, err) if reduced: red = max(1e-5, min(reduced, 0.7)) h = sfac * hstep break else: # scale the time step if possible t += h h = increase_tstep(k, err, reduced) * hstep # update to the final position xnew = np.sum(Tx, axis=0) pnew = np.sum(Tp, axis=0) gnew = np.sum(Tg, axis=0) for i in range(ntraj): if master.traj[i].active: master.traj[i].update_x(xnew[i]) master.traj[i].update_p(pnew[i]) if propphase: master.traj[i].update_phase(gnew[i]) surface.update_pes(master, update_centroids=(abs(t)>=abs(dt))) break
def fms_step_bundle(master, dt): """Propagates the wave packet using a run-time selected propagator.""" # save the bundle from previous step in case step rejected end_time = master.time + dt time_step = dt min_time_step = dt / 2.**5 while not step_complete(master.time, end_time, dt): # save the bundle from previous step in case step rejected #try: # del master0 #except NameError: # pass master0 = master.copy() # propagate each trajectory in the bundle time_step = min(time_step, end_time-master.time) # propagate amplitudes for 1/2 time step using x0 master.update_amplitudes(0.5*dt, update_ham=False) # the propagators update the potential energy surface as need be. glbl.integrator.propagate_bundle(master, time_step) # propagate amplitudes for 1/2 time step using x1 master.update_amplitudes(0.5*dt) # Renormalization if glbl.propagate['renorm'] == 1: master.renormalize() # check time_step is fine, energy/amplitude conserved accept, error_msg = check_step_bundle(master0, master, time_step) # if everything is ok.. if accept: # update the bundle time master.time += time_step # spawn new basis functions if necessary basis_grown = glbl.spawn.spawn(master, time_step) # kill the dead trajectories basis_pruned = master.prune() # if a trajectory has been added, then call update_pes # to get the electronic structure information at the associated # centroids. This is necessary in order to propagate the amplitudes # at the start of the next time step. if basis_grown and glbl.integrals.require_centroids: surface.update_pes(master) # update the Hamiltonian and associated matrices if basis_grown or basis_pruned: master.update_matrices() # re-expression of the basis using the matching pursuit # algorithm if glbl.propagate['matching_pursuit'] == 1: mp.reexpress_basis(master) # update the running log fileio.print_fms_logfile('t_step', [master.time, time_step, master.nalive]) else: # recall -- this time trying to propagate to the failed step time_step *= 0.5 fileio.print_fms_logfile('new_step', [error_msg, time_step]) if time_step < min_time_step: fileio.print_fms_logfile('general', ['minimum time step exceeded -- STOPPING.']) raise ValueError('Bundle minimum step exceeded.') # reset the beginning of the time step and go to beginning of loop #del master master = master0.copy() return master
def propagate_bundle(master, dt): """Propagates the Bundle object with RKF45.""" global h ncrd = master.traj[0].dim ntraj = master.n_traj() kx = np.zeros((ntraj, rk_ordr, ncrd)) kp = np.zeros((ntraj, rk_ordr, ncrd)) kg = np.zeros((ntraj, rk_ordr)) t = 0. if h is None: h = dt while abs(t) < abs(dt): hstep = np.sign(dt) * min(abs(h), abs(dt - t)) for rk in range(rk_ordr): tmpbundle = master.copy() for i in range(ntraj): if tmpbundle.traj[i].active: propagate_rk(tmpbundle.traj[i], hstep, rk, kx[i], kp[i], kg[i]) # update the PES to evaluate new gradients if rk < rk_ordr - 1: surface.update_pes(tmpbundle, update_centroids=False) # calculate the 4th and 5th order changes and the error dx_lo = np.zeros((master.nalive, ncrd)) dx_hi = np.zeros((master.nalive, ncrd)) dp_lo = np.zeros((master.nalive, ncrd)) dp_hi = np.zeros((master.nalive, ncrd)) dg_lo = np.zeros((master.nalive)) dg_hi = np.zeros((master.nalive)) for i in range(ntraj): if master.traj[i].active: dx_lo[i] = np.sum(wgt_lo[:, np.newaxis] * kx[i], axis=0) dx_hi[i] = np.sum(wgt_hi[:, np.newaxis] * kx[i], axis=0) dp_lo[i] = np.sum(wgt_lo[:, np.newaxis] * kp[i], axis=0) dp_hi[i] = np.sum(wgt_hi[:, np.newaxis] * kp[i], axis=0) if propphase: for i in range(ntraj): if master.traj[i].active: dg_lo[i] = np.sum(wgt_lo * kg[i]) dg_hi[i] = np.sum(wgt_hi * kg[i]) # print("a="+str(np.abs(dx_hi-dx_lo).flatten())) # print("b="+str(np.abs(dp_hi-dp_lo).flatten())) # print("c="+str(np.abs(dg_hi-dg_lo))) err = np.max( np.max(np.abs(dg_hi - dg_lo)), np.max((np.abs(dx_hi - dx_lo).flatten(), np.abs(dp_hi - dp_lo).flatten()))) else: err = np.max((np.abs(dx_hi - dx_lo), np.abs(dp_hi - dp_lo))) if err > tol: # scale the time step and try again h = hstep * max(safety * (tol / err)**0.25, 0.1) else: # scale the time step and update the position t += h err = max(err, tol * 1e-5) h *= min(safety * (tol / err)**0.2, 5.) for i in range(ntraj): if master.traj[i].active: master.traj[i].update_x(master.traj[i].x() + dx_lo[i]) master.traj[i].update_p(master.traj[i].p() + dp_lo[i]) if propphase: master.traj[i].update_phase(master.traj[i].phase() + dg_lo[i]) surface.update_pes(master, update_centroids=(abs(t) >= abs(dt)))