TBTK
HoppingAmplitudes

See more details about the HoppingAmplitude in the API

# Terminology

Consider the Schrödinger equation

$$i\hbar\frac{d}{dt}|\Psi(t)\rangle = H|\Psi(t)\rangle$$,

and the single-particle Hamiltonian

$$H = \sum_{\mathbf{i}\mathbf{j}}a_{\mathbf{i}\mathbf{j}}c_{\mathbf{i}}^{\dagger}c_{\mathbf{j}}$$.

Using finite differences, the Schrödinger equation can be rewritten as

$$|\Psi(t+dt)\rangle = \left(1 - i\hbar Hdt\right)|\Psi(t)\rangle$$.

In this form, it is clear that $$a_{\mathbf{i}\mathbf{j}}$$ is related to the rate at which single-particle processes move particles from state $$\mathbf{j}$$ to state $$\mathbf{i}$$. Due to TBTK's origin in tight-binding calculations, these parameters are called hopping amplitudes. However, we note that not all $$a_{\mathbf{i}\mathbf{j}}$$ corresponds to the movement of particles. For example, $$a_{\mathbf{i}\mathbf{i}}$$ is related to the rate at which particles are annihilated and immediately recreated in the same state. Likewise, if $$\mathbf{i}$$ and $$\mathbf{j}$$ only differ in a spin index, then the hopping amplitude is related to the rate of a spin-flip process.

The indices $$\mathbf{i}$$ and $$\mathbf{j}$$ are the state in which the particle ends up and starts out, respectively. Therefore they are referred to as to-indices and from-indices, respectively, in TBTK.

# The HoppingAmplitude class

The HoppingAmplitude class represents $$a_{\mathbf{i}\mathbf{j}}$$ and can be created as

HoppingAmplitude hoppingAmplitude(amplitude, toIndex, fromIndex);

Here amplitude is of the type std::complex<double> and toIndex and fromIndex are Indices. However, typically a HoppingAmplitude is created as an argument to a function with explicit Indices written out, in which case it looks more like

... HoppingAmplitude(amplitude, {x+1, y, spin}, {x, y, spin}) ...

Given a HoppingAmplitude, it is possible to extract the amplitude and the to- and from-Indices.

complex<double> amplitude = hoppingAmplitude.getAmplitude();
const Index &toIndex = hoppingAmplitude.getToIndex();
const Index &fromIndex = hoppingAmplitude.getFromIndex();

Sometimes it is useful to delay the specification of a HoppingAmplitudes value. This is, for example, the case when a Model is going to be solved multiple times for different parameter values. Another case is when some of the parameters in the Hamiltonian are to be determined self-consistently. For this reason, it is possible to pass a callback to the HoppingAmplitude instead of a value.

Consider a problem with the Index structure {x, y, spin} and let $$J$$ be a parameter that determines the strength of a Zeeman term. It is then possible to implement a callback as follows

class ZeemanCallback : public HoppingAmplitude::AmplitudeCallback{
public:
complex<double> getHoppingAmplitude(
const Index &to,
const Index &from
) const{
//Get spin index.
int spin = from[2];
//Return the value of the HoppingAmplitude
return -J*(1 - 2*spin);
}
void setJ(complex<double> J){
this->J = J;
}
private:
double J;
}

Here the function getHoppingAmplitude() is responsible for returning the correct value for the given combination of to- and from-Indices. Note that the spin is read from the from-Index. This is because it is assumed that this callback will only be used together with HoppingAmplitudes for which the to- and from-Indices are the same. In general, the implementation of getHoppingAmplitude() may involve looking at multiple Subindices of both Indices to figure out what to return. Finally, the setJ() function allows for the J value to be changed.

With the callback defined it is possible to create a callback dependent HoppingAmplitude.

ZeemanCallback zeemanCallback;
HoppingAmplitude up(zeemanCallback, {0, 0, 0}, {0, 0, 0});
HoppingAmplitude down(zeemanCallback, {0, 0, 1}, {0, 0, 1});
zeemanCallback.setJ(1);
Streams::out
<< up.getAmplitude() << "\t"
<< down.getAmplitude() << "\n";
Streams::out << "\n";
zeemanCallback.setJ(2);
Streams::out
<< up.getAmplitude() << "\t"
<< down.getAmplitude() << "\n";

Output:

-1
1
-2
2