Пример #1
0
def propagate_wfn(wfn, dt):
    """Propagates the Bundle object with RK4."""
    ncrd = wfn.traj[0].dim
    ntraj = wfn.n_traj()
    kx = np.zeros((ntraj, rk_ordr, ncrd))
    kp = np.zeros((ntraj, rk_ordr, ncrd))
    kg = np.zeros((ntraj, rk_ordr))

    for rk in range(rk_ordr):
        tmp_wfn = wfn.copy()
        for i in range(ntraj):
            if tmp_wfn.traj[i].active:
                propagate_rk(tmp_wfn.traj[i], dt, rk, kx[i], kp[i], kg[i])

        # update the PES to evaluate new gradients
        if rk < rk_ordr - 1:
            evaluate.update_pes(tmp_wfn, update_integrals=False)

    # update to the final position
    for i in range(ntraj):
        if wfn.traj[i].active:
            wfn.traj[i].update_x(wfn.traj[i].x() +
                                 np.sum(wgt[:, np.newaxis] * kx[i], axis=0))
            wfn.traj[i].update_p(wfn.traj[i].p() +
                                 np.sum(wgt[:, np.newaxis] * kp[i], axis=0))
            if propphase:
                wfn.traj[i].update_phase(wfn.traj[i].phase() +
                                         np.sum(wgt * kg[i, :]))
    evaluate.update_pes(wfn)
Пример #2
0
def propagate_wfn(wfn, dt):
    """Propagates the Bundle object with VV."""
    # update position
    for i in range(wfn.n_traj()):
        if wfn.traj[i].active:
            propagate_position(wfn.traj[i], dt)

    # update electronic structure for all trajectories
    # and centroids (where necessary)
    evaluate.update_pes(wfn)

    # finish update of momentum and phase
    for i in range(wfn.n_traj()):
        if wfn.traj[i].active:
            propagate_momentum(wfn.traj[i], dt)
Пример #3
0
def propagate_wfn(wfn, dt):
    """Propagates the Bundle object with BS."""
    global h
    ncrd = wfn.traj[0].dim
    ntraj = wfn.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))

        for k in range(kmax):
            tmp_wfn = wfn.copy()

            x0 = np.zeros((ntraj, ncrd))
            p0 = np.zeros((ntraj, ncrd))
            g0 = np.zeros((ntraj))
            x1 = np.zeros((ntraj, ncrd))
            p1 = np.zeros((ntraj, ncrd))
            g1 = np.zeros((ntraj))

            # step through n modified midpoint steps
            for n in range(nstep[k]):
                for i in range(ntraj):
                    if tmp_wfn.traj[i].active:
                        mm_step(tmp_wfn.traj[i], hstep / nstep[k], x0[i],
                                x1[i], p0[i], p1[i], g0[i], g1[i], n)
                evaluate.update_pes(tmp_wfn, update_integrals=False)

            # compute the modified midpoint estimate
            for i in range(ntraj):
                if wfn.traj[i].active:
                    x1[i] = 0.5 * (x1[i] + x0[i] + hstep / nstep[k] *
                                   tmp_wfn.traj[i].velocity())
                    p1[i] = 0.5 * (p1[i] + p0[i] +
                                   hstep / nstep[k] * tmp_wfn.traj[i].force())
                    if propphase:
                        g1[i] = 0.5 * (g1[i] + g0[i] + hstep / nstep[k] *
                                       tmp_wfn.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 wfn.traj[i].active:
                                wfn.traj[i].update_x(xnew[i])
                                wfn.traj[i].update_p(pnew[i])
                                if propphase:
                                    wfn.traj[i].update_phase(gnew[i])
                        evaluate.update_pes(
                            wfn, update_integrals=(abs(t) >= abs(dt)))
                        break
Пример #4
0
def step_wavefunction(dt):
    """Propagates the wave packet using a run-time selected propagator."""
    # save the wavefunction from previous step in case step rejected
    end_time = min(glbl.modules['wfn'].time + dt,
                   glbl.properties['simulation_time'])
    time_step = min(
        dt, glbl.properties['simulation_time'] - glbl.modules['wfn'].time)
    min_time_step = dt / 2.**5

    while not step_complete(glbl.modules['wfn'].time, end_time, dt):
        # save the wavefunction from previous step in case step rejected
        wfn_start = glbl.modules['wfn'].copy()

        # propagate each trajectory in the wavefunction
        time_step = min(time_step, end_time - glbl.modules['wfn'].time)

        # propagate amplitudes for 1/2 time step using x0
        glbl.modules['wfn'].update_amplitudes(0.5 * dt)

        # the propagators update the potential energy surface as need be.
        glbl.modules['propagator'].propagate_wfn(glbl.modules['wfn'],
                                                 time_step)

        # update the couplings for all the trajectories
        for i in range(glbl.modules['wfn'].n_traj()):
            glbl.modules['interface'].evaluate_coupling(
                glbl.modules['wfn'].traj[i])

        # propagate amplitudes for 1/2 time step using x1
        glbl.modules['matrices'].build(glbl.modules['wfn'],
                                       glbl.modules['integrals'])
        glbl.modules['wfn'].update_matrices(glbl.modules['matrices'])
        glbl.modules['wfn'].update_amplitudes(0.5 * dt)

        # Renormalization
        if glbl.properties['renorm'] == 1:
            glbl.modules['wfn'].renormalize()

        # check time_step is fine, energy/amplitude conserved
        accept, error_msg = check_step_wfn(wfn_start, glbl.modules['wfn'],
                                           time_step)

        # if everything is ok..
        if accept:
            # update the wavefunction time
            glbl.modules['wfn'].time += time_step
            # spawn new basis functions if necessary
            basis_grown = glbl.modules['adapt'].spawn(glbl.modules['wfn'],
                                                      time_step)
            # kill the dead trajectories
            basis_pruned = utilities.prune(glbl.modules['wfn'])

            # 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.modules['integrals'].require_centroids:
                evaluate.update_pes(glbl.modules['wfn'])

            # update the Hamiltonian and associated matrices
            if basis_grown or basis_pruned:
                glbl.modules['matrices'].build(glbl.modules['wfn'],
                                               glbl.modules['integrals'])
                glbl.modules['wfn'].update_matrices(glbl.modules['matrices'])
                for i in range(glbl.modules['wfn'].n_traj()):
                    glbl.modules['interface'].evaluate_coupling(
                        glbl.modules['wfn'].traj[i])

            # re-expression of the basis using the matching pursuit
            # algorithm
            #if glbl.properties['matching_pursuit'] == 1:
            #    mp.reexpress_basis(master)

            # update the running log
            log.print_message('t_step', [
                glbl.modules['wfn'].time, time_step, glbl.modules['wfn'].nalive
            ])
            #del wfn_start

        else:
            # recall -- this time trying to propagate to the failed step
            time_step *= 0.5
            log.print_message('new_step', [error_msg, time_step])

            if time_step < min_time_step:
                log.print_message('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
            glbl.modules['wfn'] = wfn_start.copy()
Пример #5
0
def init_wavefunction():
    """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

    log.print_message(
        'string',
        ['\n **************\n' + ' initialization\n' + ' **************\n'])

    # creat the wave function instance
    glbl.modules['wfn'] = wavefunction.Wavefunction()

    glbl.modules['interface'] = __import__('nomad.interfaces.' +
                                           glbl.methods['interface'],
                                           fromlist=['NA'])
    glbl.modules['interface'].init_interface()

    # create glbl modules
    glbl.modules['matrices'] = matrices.Matrices()

    glbl.modules['init_conds'] = __import__('nomad.initconds.' +
                                            glbl.methods['init_conds'],
                                            fromlist=['NA'])
    glbl.modules['adapt'] = __import__('nomad.adapt.' +
                                       glbl.methods['adapt_basis'],
                                       fromlist=['a'])
    glbl.modules['propagator'] = __import__('nomad.propagators.' +
                                            glbl.methods['propagator'],
                                            fromlist=['a'])

    # WARNING: this should be done more eloquently
    # need to determine form of the coefficients on the kinetic energy
    # operator, i.e. cartesian vs. normal mode coordinate basis
    # this is messy -- should be re-worked mvoing forward...
    if glbl.methods['interface'] == 'vibronic':
        # if normal modes, read frequencies from vibronic interface
        kecoef = 0.5 * glbl.modules['interface'].ham.freq
    else:
        kecoef = 0.5 / glbl.properties['crd_masses']
    glbl.modules['integrals'] = integral.Integral(
        kecoef, glbl.methods['ansatz'], glbl.methods['integral_eval'])

    # now load the initial trajectories into the bundle
    if glbl.properties['restart']:

        # print to logfile that we're restarting simulation
        log.print_message('string', [
            ' restarting simulation from checkpoint file: ' +
            str(glbl.paths['chkpt_file']) + '\n'
        ])

        # retrieve current wave function, no arguments defaults to most recent simulation
        wfn0 = None
        ints0 = None
        if glbl.mpi['rank'] == 0:
            [glbl.modules['wfn'],
             glbl.modules['integrals']] = checkpoint.retrieve_simulation(
                 time=glbl.properties['restart_time'])
            [wfn0, ints0] = checkpoint.retrieve_simulation(time=0.)

        # only root reads checkpoint file -- then broadcasts contents to other proceses
        if glbl.mpi['parallel']:
            # synchronize tasks
            glbl.mpi['comm'].barrier()
            glbl.modules['wfn'] = glbl.mpi['comm'].bcast(glbl.modules['wfn'],
                                                         root=0)
            wfn0 = glbl.mpi['comm'].bcast(wfn0, root=0)
            if glbl.modules['integrals'].require_centroids:
                glbl.modules['integrals'].centroid_required = glbl.mpi[
                    'comm'].bcast(glbl.modules['integrals'].centroid_required,
                                  root=0)
                glbl.modules['integrals'].centroids = glbl.mpi['comm'].bcast(
                    glbl.modules['integrals'].centroids, root=0)

        # save copy of t=0 wfn for autocorrelation function
        save_initial_wavefunction(wfn0)

        # check that we have all the data we need to propagate the first step -- otherwise, we
        # need to update the potential
        update_surface = False
        for i in range(glbl.modules['wfn'].n_traj()):
            if glbl.modules['wfn'].traj[i].alive and glbl.modules['wfn'].traj[
                    i].active:
                pes_data = glbl.modules['wfn'].traj[i].pes
                if ('potential' not in pes_data.avail_data()
                        or 'derivative' not in pes_data.avail_data()):
                    update_suface = True
            if glbl.modules['integrals'].require_centroids:
                for j in range(i):
                    pes_data = glbl.modules['integrals'].centroids[i][j].pes
                    if glbl.modules['integrals'].centroid_required[i][j]:
                        if ('potential' not in pes_data.avail_data()
                                and glbl.modules['wfn'].traj[i].state
                                == glbl.modules['wfn'].traj[j].state):
                            update_surface = True
                        if ('derivative' not in pes_data.avail_data()
                                and glbl.modules['wfn'].traj[i].state !=
                                glbl.modules['wfn'].traj[j].state):
                            update_surface = True
        if update_surface:
            evaluate.update_pes(glbl.modules['wfn'])
            # update the couplings for all the trajectories
            for i in range(glbl.modules['wfn'].n_traj()):
                glbl.modules['interface'].evaluate_coupling(
                    glbl.modules['wfn'].traj[i])

        # build the necessary matrices
        glbl.modules['matrices'].build(glbl.modules['wfn'],
                                       glbl.modules['integrals'])
        glbl.modules['wfn'].update_matrices(glbl.modules['matrices'])

    else:
        # first generate the initial nuclear coordinates and momenta
        # and add the resulting trajectories to the bundle
        glbl.modules['init_conds'].set_initial_coords(glbl.modules['wfn'])

        # set the initial state of the trajectories in bundle. This may
        # require evaluation of electronic structure
        set_initial_state(glbl.modules['wfn'])

        # set the initial amplitudes of the basis functions
        set_initial_amplitudes(glbl.modules['wfn'])

        # add virtual basis functions, if desired
        if glbl.properties['virtual_basis']:
            virtual_basis(glbl.modules['wfn'])

        # update the integrals
        glbl.modules['integrals'].update(glbl.modules['wfn'])

        # once phase space position and states of the basis functions
        # are set, update the potential information
        evaluate.update_pes(glbl.modules['wfn'])

        # update the couplings for all the trajectories
        for i in range(glbl.modules['wfn'].n_traj()):
            glbl.modules['interface'].evaluate_coupling(
                glbl.modules['wfn'].traj[i])

        # compute the hamiltonian matrix...
        glbl.modules['matrices'].build(glbl.modules['wfn'],
                                       glbl.modules['integrals'])
        glbl.modules['wfn'].update_matrices(glbl.modules['matrices'])

        # so that we may appropriately renormalize to unity
        glbl.modules['wfn'].renormalize()

        # this is the bundle at time t=0.  Save in order to compute auto
        # correlation function
        save_initial_wavefunction(glbl.modules['wfn'])

    # write the wavefunction to the archive
    if glbl.mpi['rank'] == 0:
        checkpoint.archive_simulation(glbl.modules['wfn'],
                                      glbl.modules['integrals'])

    log.print_message(
        'string', ['\n ***********\n' + ' propagation\n' + ' ***********\n\n'])

    log.print_message('t_step', [
        glbl.modules['wfn'].time, glbl.properties['default_time_step'],
        glbl.modules['wfn'].nalive
    ])

    return glbl.modules['wfn'].time
Пример #6
0
def propagate_wfn(wfn, dt):
    """Propagates the Bundle object with RKF45."""
    global h
    ncrd = wfn.traj[0].dim
    ntraj = wfn.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):
            tmp_wfn = wfn.copy()
            for i in range(ntraj):
                if tmp_wfn.traj[i].active:
                    propagate_rk(tmp_wfn.traj[i], hstep, rk, kx[i], kp[i],
                                 kg[i])

            # update the PES to evaluate new gradients
            if rk < rk_ordr - 1:
                evaluate.update_pes(tmp_wfn, update_integrals=False)

        # calculate the 4th and 5th order changes and the error
        dx_lo = np.zeros((wfn.nalive, ncrd))
        dx_hi = np.zeros((wfn.nalive, ncrd))
        dp_lo = np.zeros((wfn.nalive, ncrd))
        dp_hi = np.zeros((wfn.nalive, ncrd))
        dg_lo = np.zeros((wfn.nalive))
        dg_hi = np.zeros((wfn.nalive))
        for i in range(ntraj):
            if wfn.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 wfn.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 wfn.traj[i].active:
                    wfn.traj[i].update_x(wfn.traj[i].x() + dx_lo[i])
                    wfn.traj[i].update_p(wfn.traj[i].p() + dp_lo[i])
                    if propphase:
                        wfn.traj[i].update_phase(wfn.traj[i].phase() +
                                                 dg_lo[i])
            evaluate.update_pes(wfn, update_integrals=(abs(t) >= abs(dt)))