qtcad.device.operators module

Operators represented in solved eigenbases.

The classes in this module build low-dimensional matrix representations of position-dependent operators in the eigenbasis of a device or of another operator.

Currently, only 3D systems are supported.

class Gate(d: Device | SubDevice | Operator, gate: str | list[str], V: float | list[float], phys_d: Device | None = None, NL: bool = True, params: SolverParams | SolverParams | None = None, base_bias: float | list[float] | None = None)

Bases: Gate

Represent the operator induced by changing gate voltages.

The operator is obtained by deep-copying the physical device, changing the requested gate voltages on the copy, solving the Poisson equation on the copy, and computing the potential change delta_phi relative to the base configuration. The stored operator is U = +/- e * delta_phi, with the sign set by the confined carrier type: + for holes and - for electrons.

Attributes:
  • phys_d (device object) – Physical device describing the full system on which the Poisson boundary conditions are enforced.

  • solver (PoissonSolver) – Poisson solver to be used in the calculation of U_matrix. It will either be a linear or a non-linear Poisson solver.

  • params (SolverParams) – Solver parameters to pass to the Poisson solver.

Note

Because the Gate class is a subclass of the HamiltonianOperator class it has the attributes and methods of the HamiltonianOperator class in addition to the ones listed here.

__init__(d: Device | SubDevice | Operator, gate: str | list[str], V: float | list[float], phys_d: Device | None = None, NL: bool = True, params: SolverParams | SolverParams | None = None, base_bias: float | list[float] | None = None) None

Initialize a gate-induced Hamiltonian perturbation.

Parameters:
  • d – Object providing mesh, energies, and eigenfunctions. Typically, this is a device for which eigenstates have been computed with the Schrödinger solver. It may also be another Operator.

  • gate – Label or labels of the gates whose voltages are modified.

  • V – Voltage modulation applied to each gate. This input must be consistent with gate. If gate is a string, V must be a float giving the voltage applied to that gate. If gate is a list of strings, V must be a list of floats whose entries give the voltages applied to the corresponding gates.

  • phys_d – Physical device on which the gates are defined. If None, the physical device is assumed to be d.

  • NL – Whether to use the nonlinear Poisson solver. If False, the linear Poisson solver is used.

  • params – Solver parameters passed to the Poisson solver.

  • base_bias – Base bias configuration, giving the gate voltages before the modulation is applied. If gate is a string, base_bias must be a float. If gate is a list of strings, base_bias must be a list of floats whose entries correspond to the gates in gate. If None, the base configuration stored in the device is used.

class HamiltonianOperator(d: Device | SubDevice | Operator, U: Callable[[float, float, float], float | complex | ndarray[tuple[Any, ...], dtype[_ScalarT]]] | ndarray[tuple[Any, ...], dtype[_ScalarT]])

Bases: HamiltonianOperator

Represent an operator used as a perturbation to a Hamiltonian.

A HamiltonianOperator represents a perturbation \(U\) applied to an unperturbed Hamiltonian \(H_0\). The basis eigenfunctions is assumed to be the eigenbasis of \(H_0\); \(H_0\) is constructed by placing energies on the diagonal of a matrix. The perturbation \(U\) is constructed from the matrix elements of the input operator. The solve method diagonalizes \(H = H_0 + U\) in the input eigenbasis. It can only be called once on a given instance.

Attributes:
  • eigenfunctions (numpy array) – Eigenfunctions of the system. If solve has not been called, this stores the unperturbed eigenfunctions. If solve has been called, this stores the eigenfunctions after diagonalizing \(H_0 + U\); the input basis is stored in eigenfunctions_old. The first axis runs over mesh nodes, the second over eigenstates, and the third, if present, over bands such as spin, valley, or valence bands.

  • energies (numpy array) – Eigenenergies of the system. If solve has not been called, this stores the unperturbed eigenenergies. If solve has been called, this stores the eigenenergies after diagonalizing \(H_0 + U\); the input eigenenergies are stored in energies_old.

  • states (numpy array) – 2D array whose columns correspond to the eigenvectors of the perturbed system, i.e. \(H_0 + U\), in the subspace spanned by eigenfunctions_old. Here \(H_0\) is the matrix with energies_old on its diagonal.

  • eigenfunctions_old (numpy array) – Eigenbasis of the system prior to diagonalizing \(H_0 + U\), with the same axis layout as the eigenfunctions input.

  • energies_old (numpy array) – Eigenenergies of the system prior to diagonalizing \(H_0 + U\).

  • solved (boolean) – Whether solve has been called on this object. Calling solve twice on the same object is not allowed. To apply another perturbation, construct a new operator from the solved object.

Note

Because HamiltonianOperator is a subclass of Operator, it also provides the attributes and methods inherited from Operator.

__init__(d: Device | SubDevice | Operator, U: Callable[[float, float, float], float | complex | ndarray[tuple[Any, ...], dtype[_ScalarT]]] | ndarray[tuple[Any, ...], dtype[_ScalarT]]) None

Initialize a Hamiltonian perturbation on an eigenbasis.

Parameters:
  • d – Object providing mesh, energies, and eigenfunctions. Typically this is a device for which eigenstates have been computed with the Schrödinger solver. It may also be another Operator.

  • U – Hamiltonian perturbation values defined over all global nodes. If U is a function, it must take the \(x\), \(y\), and \(z\) coordinates as arguments and return either a scalar for scalar eigenfunctions or a band matrix for eigenfunctions with a band axis. If U is a NumPy array, it must have shape (nodes,) for scalar eigenfunctions, or shape (nodes, bands, bands) for eigenfunctions with a band axis, where nodes is the number of global nodes in the mesh.

solve() None

Compute new energies and eigenstates by diagonalizing \(H_0 + U\).

\(H_0\) is a diagonal matrix with energies on its diagonal, and \(U\) is the Hamiltonian perturbation matrix. Before diagonalization, \(U\) is validated as Hermitian.

This method updates energies and eigenfunctions and stores their pre-solve values in energies_old and eigenfunctions_old.

Raises:
  • RuntimeError – If called more than once on the same object.

  • ValueError – If the perturbation matrix is not Hermitian.

class Operator(d: Device | SubDevice | Operator, U: Callable[[float, float, float], float | complex | ndarray[tuple[Any, ...], dtype[_ScalarT]]] | ndarray[tuple[Any, ...], dtype[_ScalarT]])

Bases: Operator

Represent an operator in a device eigenbasis.

The input basis is usually taken from a Device after the Schrödinger equation has been solved, but it can also be taken from another Operator object. Eigenfunctions are expected to have shape (nodes, states) for scalar eigenfunctions or (nodes, states, band) when a band axis is present (bands can refer to spin, valley, or any other components).

Attributes:
  • eigenfunctions (numpy array) – Eigenfunctions of the system.

  • energies (numpy array) – Eigenenergies of the system.

  • mesh (Mesh object) – Mesh over which the eigenfunctions are defined.

  • local (boolean) – Whether integrals are computed over local tetrahedral nodes. If not, they are computed over global nodes.

  • multiband (boolean) – Whether the eigenfunctions have a band axis.

  • U (numpy array) – Operator defined over the mesh.

  • U_matrix (2D numpy array) – Finite-dimensional matrix representation of U in the basis used when get_operator_matrix is first called.

  • solved (boolean) – Whether solve has already been called on this operator object. New operators constructed from solved operators start with solved = False.

__init__(d: Device | SubDevice | Operator, U: Callable[[float, float, float], float | complex | ndarray[tuple[Any, ...], dtype[_ScalarT]]] | ndarray[tuple[Any, ...], dtype[_ScalarT]]) None

Initialize an operator on a device eigenbasis.

Parameters:
  • d – Object providing mesh, energies, and eigenfunctions. Typically this is a Device for which eigenstates have been computed with the Schrödinger solver. Can also be another Operator object.

  • U – Operator values defined over all global nodes. If U is a function, it must take the \(x\), \(y\), and \(z\) coordinates as arguments and return either a scalar for scalar eigenfunctions or a band matrix for eigenfunctions with a band axis. If U is a NumPy array, it must have shape (nodes,) for scalar eigenfunctions, or shape (nodes, bands, bands) for eigenfunctions with a band axis, where nodes is the number of global nodes in the mesh.

get_operator_matrix() ndarray[tuple[Any, ...], dtype[_ScalarT]]

Return the matrix representation of the operator.

If self.U_matrix already exists, it is returned unchanged. Otherwise, all matrix elements are computed explicitly in the basis currently stored in self.eigenfunctions, assigned to self.U_matrix, and returned. Changing the attributes eigenfunctions, energies, or U after this call does not update self.U_matrix. To represent the same operator in a different basis, construct a new operator from an object that stores that basis.

Returns:

Matrix representation of the operator in the subspace defined by self.eigenfunctions.

get_operator_matrix_element(i: int, j: int) float | complex

Compute a single matrix element of the operator U.

Parameters:
  • i – Row index of the matrix element we wish to compute. Indices are based on the ordering of self.eigenfunctions.

  • j – Column index of the matrix element we wish to compute. Indices are based on the ordering of self.eigenfunctions.

Returns:

Matrix element of the matrix representation of the operator.

Note

Matrix elements are assembled by integrating \(\psi_i^* U \psi_j\). For states with a band axis, band indices are contracted. Here, \(\psi_i\) is given by self.eigenfunctions[:, i, ...].

class Zeeman(d: Device | SubDevice | Operator, B: Callable[[float, float, float], ndarray[tuple[Any, ...], dtype[_ScalarT]]] | ndarray[tuple[Any, ...], dtype[_ScalarT]], g: Callable[[float, float, float], ndarray[tuple[Any, ...], dtype[_ScalarT]]] | ndarray[tuple[Any, ...], dtype[_ScalarT]] | tuple | None = None)

Bases: Zeeman

Represent the Zeeman operator in a device eigenbasis.

Supported bases are spinless electron eigenstates, two-band electron spin bases, and four-band hole Luttinger-Kohn bases. For spinless electron eigenstates, this operator adds a spin axis before constructing the perturbation.

Attributes:
  • B (numpy array) – Magnetic field defined over all global nodes if self.local is False, or over local tetrahedral nodes if self.local is True.

  • g (numpy array) – g-tensor defined over all global nodes if self.local is False, or over local tetrahedral nodes if self.local is True. Only initialized for electron calculations.

  • kappa (numpy array) – Hole Zeeman parameter \(\kappa\) defined over all global nodes if self.local is False, or over local tetrahedral nodes if self.local is True. Only initialized for hole calculations.

  • q (numpy array) – Hole Zeeman parameter \(q\) defined over all global nodes if self.local is False, or over local tetrahedral nodes if self.local is True. Only initialized when specified for hole calculations. If not specified, \(q\) is assumed to be zero at all nodes.

  • eigenfunctions_no_spin (numpy array) – Eigenfunctions before adding the spin index. Only used in spinless electron calculations.

  • energies_no_spin (numpy array) – Energies before adding the spin index. Only used in spinless electron calculations.

Note

Because Zeeman is a subclass of HamiltonianOperator, it also provides the attributes and methods inherited from HamiltonianOperator.

__init__(d: Device | SubDevice | Operator, B: Callable[[float, float, float], ndarray[tuple[Any, ...], dtype[_ScalarT]]] | ndarray[tuple[Any, ...], dtype[_ScalarT]], g: Callable[[float, float, float], ndarray[tuple[Any, ...], dtype[_ScalarT]]] | ndarray[tuple[Any, ...], dtype[_ScalarT]] | tuple | None = None) None

Initialize the Zeeman operator.

Parameters:
  • d – Object providing mesh, energies, and eigenfunctions. Typically, this is a device for which eigenstates have been computed with the Schrödinger solver. It may also be another Operator.

  • B – Magnetic field. If callable, B must take the coordinates \(x\), \(y\), and \(z\) as arguments and return the magnetic field at that point. B may also be a one-dimensional array of length 3 representing a constant magnetic field, or a two-dimensional array with shape (nodes, 3), where nodes is the number of global nodes in the mesh.

  • g – Zeeman coupling parameters.

    For electrons, g represents the g-tensor. If callable, it must take the coordinates \(x\), \(y\), and \(z\) as arguments and return the g-tensor at that point. g may also be a (3, 3) array representing a constant g-tensor, an array with shape (nodes, 3, 3) for a node-dependent g-tensor, where nodes is the number of global nodes in the mesh, or, for 3D tetrahedral meshes, a local array with shape (tetra_number, local_node_number, 3, 3). The g-tensor convention is \(H_Z = \mu_B \sum_{i,j} S_i g_{ij} B_j\); the first g-tensor index contracts with the vector of spin operators \(\mathbf{S}=\left(S_x, S_y, S_z\right)\) and the second contracts with the magnetic field \(\mathbf{B}. If ``g`\) is None, it is taken from d.get_electron_g_factor().

    For holes, g may be a tuple (kappa, q), where kappa gives the Zeeman coupling to the spin-3/2 matrices \(J_i\) and q gives the Zeeman coupling to the cubed spin-3/2 matrices \(J_i^3\). If g is not a tuple, \(q\) is assumed to be zero. If g is None, kappa and q are taken from d.get_hole_Zeeman_params().