def build_tau(self): """Build the nonadiabatic coupling matrix, tau This routine assumes that S is already built""" c1i = (complex(0.0, 1.0)) cm1i = (complex(0.0, -1.0)) ntraj = self.get_num_traj_qm() self.tau = np.zeros((ntraj, ntraj), dtype=np.complex128) for key in self.centroids: keyi, keyj = str.split(key, "_a_") i = self.traj_map[keyi] j = self.traj_map[keyj] if i < ntraj and j < ntraj: istate = self.centroids[key].get_istate() jstate = self.centroids[key].get_jstate() if istate != jstate: Sij = cg.overlap_nuc(self.traj[keyi], self.traj[keyj], positions_i="positions_qm", positions_j="positions_qm", momenta_i="momenta_qm", momenta_j="momenta_qm") tdc = self.centroids[key].get_timederivcoups_qm()[jstate] self.tau[i, j] = Sij * cm1i * tdc self.tau[j, i] = Sij.conjugate() * c1i * tdc
def update_centroids(self): """Compute the centroid positions and moment and check which centroids can be computed""" for key in self.centroids: key1, key2 = str.split(key, "_a_") timestep = self.centroids[key].get_timestep() # update backpropagating centroids self.centroids[key].set_z_compute_me_backprop(False) backprop_time = self.centroids[key].get_backprop_time() - timestep if (self.centroids[key].get_mintime() - 1.0e-6) < backprop_time: backprop_time1 = self.traj[key1].get_backprop_time() if (backprop_time > backprop_time1 - 1.0e-6)\ and (backprop_time1 < (self.traj[key1].get_firsttime() - 1.0e-6)\ or backprop_time1 < (self.traj[key1].get_mintime() + 1.0e-6)): backprop_time2 = self.traj[key2].get_backprop_time() if (backprop_time > backprop_time2 - 1.0e-6)\ and (backprop_time2 < (self.traj[key2].get_firsttime() - 1.0e-6)\ or backprop_time2 < (self.traj[key2].get_mintime() + 1.0e-6)): time1 = self.traj[key1].get_time() time2 = self.traj[key2].get_time() # this if takes care of the special case where we try # to compute the backpropagating centroid at firsttime before # forward propagation has begun if (backprop_time + 1.0e-6 < time1) and (backprop_time + 1.0e-6 < time2): pos1 = self.traj[key1].get_data_at_time_from_h5( backprop_time, "positions") mom1 = self.traj[key1].get_data_at_time_from_h5( backprop_time, "momenta") # if backprop_time2 < (self.traj[key2].get_mintime() + 1.0e-6): # pos2 = self.traj[key2].get_backprop_positions() # mom2 = self.traj[key2].get_backprop_momenta() # else: pos2 = self.traj[key2].get_data_at_time_from_h5( backprop_time, "positions") mom2 = self.traj[key2].get_data_at_time_from_h5( backprop_time, "momenta") # pos2, mom2 = self.traj[key2].get_q_and_p_at_time_from_h5(backprop_time) absSij = abs( cg.overlap_nuc(self.traj[key1], self.traj[key2], positions_i=pos1, positions_j=pos2, momenta_i=mom1, momenta_j=mom2)) # print "absSij", absSij # this definition of mom is only right if all basis functions have same # width!!!! I don't think the momentum is every used but still we # should fix this soon. width1 = self.traj[key1].get_widths() width2 = self.traj[key2].get_widths() pos_cent = (width1 * pos1 + width2 * pos2) / (width1 + width2) mom_cent = 0.5 * (mom1 + mom2) self.centroids[key].set_backprop_positions( pos_cent) self.centroids[key].set_backprop_momenta(mom_cent) if absSij > 0.001: self.centroids[key].set_z_compute_me_backprop( True) else: self.centroids[key].set_backprop_time( backprop_time) dt = self.centroids[key].get_timestep() self.centroids[ key].set_backprop_time_half_step( backprop_time + 0.5 * dt) self.centroids[key].set_backprop_energies( np.zeros( self.centroids[key].get_numstates())) self.centroids[key].set_backprop_timederivcoups( np.zeros( self.centroids[key].get_numstates())) firsttime = self.centroids[key].get_firsttime() if abs(backprop_time - firsttime) > 1.0e-6: self.centroids[key].h5_output(True) # update forward propagating centroids self.centroids[key].set_z_compute_me(False) time = self.centroids[key].get_time() + timestep if (self.centroids[key].get_maxtime() + timestep + 1.0e-6) > time: time1 = self.traj[key1].get_time() if (time < time1 + 1.0e-6 ) and time1 > self.traj[key1].get_firsttime() + 1.0e-6: time2 = self.traj[key2].get_time() if (time < time2 + 1.0e-6 ) and time2 > self.traj[key2].get_firsttime() + 1.0e-6: pos1 = self.traj[key1].get_data_at_time_from_h5( time, "positions") mom1 = self.traj[key1].get_data_at_time_from_h5( time, "momenta") pos2 = self.traj[key2].get_data_at_time_from_h5( time, "positions") mom2 = self.traj[key2].get_data_at_time_from_h5( time, "momenta") #pos1, mom1 = self.traj[key1].get_q_and_p_at_time_from_h5(time) #pos2, mom2 = self.traj[key2].get_q_and_p_at_time_from_h5(time) absSij = abs( cg.overlap_nuc(self.traj[key1], self.traj[key2], positions_i=pos1, positions_j=pos2, momenta_i=mom1, momenta_j=mom2)) #print "absSij", absSij # this definion of mom is only correct if all basis functions have same # width!!!! I don't think that the centroid momentum is ever used, but # we should still fix this soon width1 = self.traj[key1].get_widths() width2 = self.traj[key2].get_widths() pos_cent = (width1 * pos1 + width2 * pos2) / (width1 + width2) mom_cent = 0.5 * (mom1 + mom2) self.centroids[key].set_positions(pos_cent) self.centroids[key].set_momenta(mom_cent) if absSij > 0.001: self.centroids[key].set_z_compute_me(True) else: self.centroids[key].set_time(time) dt = self.centroids[key].get_timestep() self.centroids[key].set_time_half_step(time - 0.5 * dt) self.centroids[key].set_energies( np.zeros(self.centroids[key].get_numstates())) self.centroids[key].set_timederivcoups( np.zeros(self.centroids[key].get_numstates())) firsttime = self.centroids[key].get_firsttime() if abs(time - firsttime) > 1.0e-6: self.centroids[key].h5_output(False) else: self.centroids[key].h5_output( False, zdont_half_step=True)
def spawn_as_necessary(self): """this is the spawning routine""" spawntraj = dict() for key in self.traj: # trajectories that are spawning or should start were marked # during propagation. See "propagate_step" and "consider_spawning" # in traj.py z = self.traj[key].get_z_spawn_now() z_dont = self.traj[key].get_z_dont_spawn() spawnt = self.traj[key].get_spawntimes() for jstate in range(self.traj[key].get_numstates()): # is this trajectory marked to spawn to state j? if z[jstate] > 0.5: # create label that indicates parentage # for example: # a trajectory labeled 00b1b5 means that the initial # trajectory "00" spawned a trajectory "1" (its # second child) which then spawned another (it's 6th child) label = str(self.traj[key].get_label() + "b" + str(self.traj[key].get_numchildren())) # create and initiate new trajectory structure newtraj = traj(self.traj[key].numdims, self.traj[key].numstates) newtraj.init_spawn_traj(self.traj[key], jstate, label) # checking if overlap between parent and child is not too small # sometimes NAC coupling jumps at points of electronic wf discontinuity # even though it is a warning sign, it is inevitable in many cases # so here we calculate nuclear overlap to make sure there is going to # be population transfer as a result of adding newtraj parent_child_nuc_olap = cg.overlap_nuc( self.traj[key], newtraj) if np.abs(parent_child_nuc_olap) < 0.05: z_add_traj_olap = False else: z_add_traj_olap = True # checking to see if overlap with existing trajectories # is too high. If so, we abort spawn if z_add_traj_olap: z_add_traj_olap = self.check_overlap(newtraj) # rescaling velocity. We'll abort if there is not # enough energy (aka a "frustrated spawn") z_add_traj_rescale = newtraj.rescale_momentum( self.traj[key].get_energies_tmdt()[ self.traj[key].get_istate()]) # okay, now we finally decide whether to spawn or not if z_add_traj_olap and z_add_traj_rescale: print "## creating new trajectory ", label spawntraj[label] = newtraj self.traj[key].incr_numchildren() # whether we spawn or not, we reset the trajectory so # that: # it isn't slated to spawn z[jstate] = 0.0 # it shouldn't spawn again until the coupling drops # below a threshold z_dont[jstate] = 1.0 # it isn't currently spawning spawnt[jstate] = -1.0 # once all states have been checked, we update the TBF structure self.traj[key].set_z_spawn_now(z) self.traj[key].set_z_dont_spawn(z_dont) self.traj[key].set_spawntimes(spawnt) # okay, now it's time to add the spawned trajectories for label in spawntraj: # create new centroid structures for key2 in self.traj: # "_a_" marks the centroid labels centkey = str(key2 + "_a_" + label) # create and initiate the trajectory structures! newcent = traj(self.traj[key].numdims, self.traj[key].numstates) newcent.init_centroid(self.traj[key2], spawntraj[label], centkey) # add the centroid self.centroids[centkey] = newcent print "# adding centroid ", centkey # finally, add the spawned trajectory self.add_traj(spawntraj[label])