Dear Kwant developers,

I was wondering if there is a limit on the resolution of Kwant and Tkwant when 
calculating the density of an evolved wavefunction.

I am currently working on a code to simulate the response of a carbon nanotube 
spin qubit to an electric dipole spin resonance drive. I am using Kwant to 
define the carbon nanotube as a scattering region and then apply a Hamiltonian 
to the system. The lowest eigenvector of the Hamiltonian at t=0 is determined 
and associated with a tkwant.onebody.WaveFunction. This wavefunction is evolved 
over time and the density of the wavefunction calculated using 
kwant.operator.Density.

The relevant part of my code is given below:

class System:
    def __init__(self, hamiltonian, pertubation_type, number_of_lattices, 
potential_type):
        self.potential_type = potential_type
        self.hamiltonian = hamiltonian
        self.number_of_lattices = number_of_lattices
        self.pertubation_type = pertubation_type
        self.evolve_state = False
       
    def potential(self, z, time):
        if self.potential_type == 0: #infinite square well
            total_potential = 0  
        elif self.potential_type == 1: #parabolic potential
            total_potential = .5 * (z * self.omega_0_au) ** 2        
        if self.pertubation_type == "cos":
            self.pertubation = self.cosine_v_ac
        else:
            self.pertubation = self.sine_v_ac
        total_potential += self.pertubation(time, z, self.eV_0_au, 
self.pulse_frequency_au, self.total_length_au)
        return total_potential

    def kwant_shape(self, site):
        (z,) = site.pos
        return (-self.total_length_au / 2 <= z < self.total_length_au / 2)

    def make_system(self):
        self.template = kwant.continuum.discretize(self.hamiltonian, 
grid=self.lattice_size_au)  
        self.syst = kwant.Builder()
        # add the nanotube to the system
        self.syst.fill(self.template, self.kwant_shape, (0,))
        self.syst = self.syst.finalized()

        def B_constant(z):
            return  -self.g * self.mu_B_au * self.B_z_au * self.hbar_au / 2
        def C_constant(z):
            return -self.g * self.mu_B_au * self.b_sl_au * z * self.hbar_au / 2
        def D_constant(z):
            return -self.g * self.mu_B_au * self.B_y_au * self.hbar_au / 2

        # coefficient for the kinetic energy term
        A_constant = self.hbar_au ** 2 / (2 * self.m_au)

        # parameters of hamiltonian
        self.params = dict(A=A_constant, V=self.potential, B=B_constant, 
C=C_constant, D=D_constant)
        self.tparams = self.params.copy()  # copy the params array
        self.tparams['time'] = 0  # initial time = 0

        # compute the Hamiltonian matrix
        hamiltonian = self.syst.hamiltonian_submatrix(params=self.tparams)
        # compute the eigenvalues (energies) and eigenvectors (wavefunctions).
        eigenValues, eigenVectors = np.linalg.eig(hamiltonian)

        # sort the eigenvectors and eigenvalues according the ascending 
eigenvalues.
        idx = eigenValues.argsort()
        self.initial_eigenvalues = eigenValues[idx]
        eigenVectors = eigenVectors[:, idx]

        # initial wave functions unperturbed by time dependent part of 
hamiltonian
        self.psi_1_init = eigenVectors[:, 0]
        self.psi_2_init = eigenVectors[:, 1]

        # an object representing the spin-up state
        self.spin_up_state = 
tkwant.onebody.WaveFunction.from_kwant(syst=self.syst,
                                                                    
psi_init=self.psi_1_init,
                                                                    
energy=eigenValues[0],
                                                                    
params=self.params)
        return self.syst

    def evolve(self, time_steps):  
        self.evolve_state = True
       
        total_osc_time = self.second_to_au(0.5e-8)
        times_au = np.linspace(0, total_osc_time, num=time_steps)
        times_SI = np.linspace(0, self.au_to_second(total_osc_time), 
num=time_steps)

        density_operator = kwant.operator.Density(self.syst)  
        psi = self.spin_up_state
       
        data = {'states': []}

        for time in times_au:
            psi.evolve(time)  # evolve the wavefunction
            density = psi.evaluate(density_operator) # compute density

            data['states'].append({'pdf': density.tolist()})
       
        json_string = json.dumps(data)
        with open(r'\{}.json'.format(timestr), 'w') as outfile:
            outfile.write(json_string)

        self.evolve_state = False

        return True
   
    def main():
        lattices = 100
        potential = 0
        system = System("((A * k_z**2) + V(z, time)) * identity(2) + B(z) * 
sigma_z + C(z) * sigma_x + D(z) * sigma_y",
                        pertubation_type="sin", number_of_lattices=lattices,
                        potential_type=potential)
        system.make_system()

        system.evolve(10)

if __name__ == '__main__':
    main()

Please note that I haven't included some conversion functions in the above code.

Using the results from this code, I can obtain a graph of density of the 
wavefunction across the carbon nanotube evolving with time. The graph shows an 
expected smooth Gaussian response for the first few time steps, however the 
graph then becomes very noisy and spiky. I was wondering why this occurs and if 
it could be due to a limit on the resolution of Tkwant?

In addition, I was wondering if you had any advice for making the code run 
faster? I ran the simulation for 10 time steps over a very short time period to 
obtain the attached animation and it took around 4 hours on my desktop.

Thank you for any help with this.

Best wishes,
Isobel

Reply via email to