Skip to content

Potentials

Generated from the docstrings in the source code.

create_potential(name: str, **kwargs) -> Potential

Source code in quantumlab/potentials/__init__.py
def create_potential(name: str, **kwargs) -> Potential:
    name_lower = name.lower()
    if name_lower not in POTENTIAL_REGISTRY:
        raise ValueError(f"Potential '{name}' not found. Available: {list(POTENTIAL_REGISTRY.keys())}")
    return POTENTIAL_REGISTRY[name_lower](**kwargs)

base

traps

HarmonicTrap(omegas, m: float = 1.0, center=None)

Bases: Potential

Anisotropic harmonic trap in 1D, 2D or 3D:

V(r) = ½ m Σ_j ω_j² (r_j − c_j)²

Parameters:

Name Type Description Default
omegas float or sequence of float

Trap frequency per axis (a scalar gives an isotropic trap).

required
m float

Particle mass.

1.0
center sequence of float

Trap centre per axis. Defaults to the origin.

None
Source code in quantumlab/potentials/traps.py
def __init__(self, omegas, m: float = 1.0, center=None):
    self.omegas = omegas
    self.m = m
    self.center = center

coulomb

SoftCoulomb(Z: float = 1.0, softening: float = np.sqrt(2.0), center=None)

Bases: Potential

Soft-core Coulomb potential of a nucleus of charge Z at center:

V(r) = −Z / sqrt(|r − c|² + a²)

The softening a removes the singularity so the potential is resolvable on a grid. In 1D, Z = 1 and a² = 2 give a ground-state energy of −0.5 a.u., the ionisation potential of hydrogen, which makes it the standard single-active-electron model for strong-field physics.

Source code in quantumlab/potentials/coulomb.py
def __init__(self, Z: float = 1.0, softening: float = np.sqrt(2.0), center=None):
    self.Z = Z
    self.softening = softening
    self.center = center

gradient(grid)

Analytic ∇V as a tuple of arrays (∂V/∂x[, ∂V/∂y[, ∂V/∂z]]).

Source code in quantumlab/potentials/coulomb.py
def gradient(self, grid):
    """Analytic ∇V as a tuple of arrays (∂V/∂x[, ∂V/∂y[, ∂V/∂z]])."""
    d = self._displacements(grid)
    r2 = sum(di ** 2 for di in d)
    factor = self.Z / (r2 + self.softening ** 2) ** 1.5
    return tuple(factor * di for di in d)

apertures

ApertureScreen(V0: float, wall_position: float = 0.0, wall_thickness: float = 1.0, separation: float = 4.0, aperture_width: float = 1.0, n_apertures: int = 2, aperture: str = 'slit', smoothing: float = 0.25)

Bases: Potential

Opaque screen perpendicular to x with N equally spaced openings (multi-slit / multi-pinhole interferometer) in 2D or 3D.

The wall occupies wall_position ± wall_thickness/2. Edges are smoothed over smoothing length units so the potential stays resolvable on the FFT grid. Openings are centred at y_i = (i − (N−1)/2) · separation.

Parameters:

Name Type Description Default
V0 float

Wall height. Choose V0 well above the incident kinetic energy so the wall is effectively opaque.

required
wall_position float

Location and thickness of the screen along x.

0.0
wall_thickness float

Location and thickness of the screen along x.

0.0
separation float

Centre-to-centre distance between neighbouring openings.

4.0
aperture_width float

Slit width (or pinhole diameter).

1.0
n_apertures int

Number of openings (2 = double slit).

2
aperture (slit, pinhole)

In 3D: 'slit' openings extend along z, 'pinhole' openings are circular holes centred at z = 0. Ignored in 2D.

'slit'
smoothing float

Edge-smoothing length.

0.25
Source code in quantumlab/potentials/apertures.py
def __init__(self, V0: float, wall_position: float = 0.0, wall_thickness: float = 1.0,
             separation: float = 4.0, aperture_width: float = 1.0, n_apertures: int = 2,
             aperture: str = 'slit', smoothing: float = 0.25):
    if aperture not in ('slit', 'pinhole'):
        raise ValueError("aperture must be 'slit' or 'pinhole'")
    self.V0 = V0
    self.wall_position = wall_position
    self.wall_thickness = wall_thickness
    self.separation = separation
    self.aperture_width = aperture_width
    self.n_apertures = n_apertures
    self.aperture = aperture
    self.smoothing = smoothing

StadiumBilliard(V0: float, radius: float, length: float, smoothing: float = 0.2)

Bases: Potential

Soft-walled Bunimovich stadium in 2D: the set of points within distance R of the segment [−L/2, L/2] on the x axis. The classical stadium billiard is fully chaotic; its quantum eigenstates show the well-known scarring.

V(x, y) = V0 · S((dist(x, y) − R) / s)

where S is a tanh step and dist the distance to the central segment.

Source code in quantumlab/potentials/apertures.py
def __init__(self, V0: float, radius: float, length: float, smoothing: float = 0.2):
    self.V0 = V0
    self.radius = radius
    self.length = length
    self.smoothing = smoothing

time_dependent

Explicitly time-dependent potentials V(r, t).

Solvers detect these through the time_dependent flag and call evaluate_at(grid, t) at every (sub)step instead of caching V once.

DipoleDrive(base: Optional[Potential], F0: float, omega: float, axis: str = 'x', phase: float = 0.0, envelope: Optional[Callable[[float], float]] = None)

Bases: TimeDependentPotential

Static potential plus a spatially uniform oscillating force along one axis (dipole approximation, length gauge):

V(r, t) = V_base(r) − F(t) · r_axis,
F(t)    = F0 · envelope(t) · sin(ω t + φ)

For a particle of charge q in an electric field E(t), F(t) = q E(t).

Parameters:

Name Type Description Default
base Potential or None

Static background potential (None means free space).

required
F0 float

Peak force amplitude.

required
omega float

Driving angular frequency.

required
axis (x, y, z)

Direction of the force.

'x'
phase float

Carrier phase φ.

0.0
envelope callable

envelope(t) -> float, e.g. sin2_envelope(T). Defaults to 1.

None
Source code in quantumlab/potentials/time_dependent.py
def __init__(self, base: Optional[Potential], F0: float, omega: float,
             axis: str = 'x', phase: float = 0.0,
             envelope: Optional[Callable[[float], float]] = None):
    if axis not in _AXIS_INDEX:
        raise ValueError("axis must be 'x', 'y' or 'z'")
    self.base = base
    self.F0 = F0
    self.omega = omega
    self.axis = axis
    self.phase = phase
    self.envelope = envelope
    self._cache_grid = None
    self._cache = None

TimeDependentCustomPotential(func: Callable)

Bases: TimeDependentPotential

User-defined V(r, t): func(x, t), func(X, Y, t) or func(X, Y, Z, t).

Source code in quantumlab/potentials/time_dependent.py
def __init__(self, func: Callable):
    if not callable(func):
        raise TypeError('func must be callable')
    self.func = func

sin2_envelope(duration: float) -> Callable[[float], float]

Smooth sin² pulse envelope that is non-zero only on 0 ≤ t ≤ duration.

Source code in quantumlab/potentials/time_dependent.py
def sin2_envelope(duration: float) -> Callable[[float], float]:
    """Smooth sin² pulse envelope that is non-zero only on 0 ≤ t ≤ duration."""
    def envelope(t: float) -> float:
        if t <= 0.0 or t >= duration:
            return 0.0
        return float(np.sin(np.pi * t / duration) ** 2)
    return envelope

flat_top_envelope(duration: float, ramp: float) -> Callable[[float], float]

Pulse envelope with sin² turn-on and turn-off of length ramp and a flat top in between; zero outside 0 ≤ t ≤ duration. Long flat tops give narrow, well-resolved harmonic lines.

Source code in quantumlab/potentials/time_dependent.py
def flat_top_envelope(duration: float, ramp: float) -> Callable[[float], float]:
    """
    Pulse envelope with sin² turn-on and turn-off of length ``ramp`` and a
    flat top in between; zero outside 0 ≤ t ≤ duration. Long flat tops give
    narrow, well-resolved harmonic lines.
    """
    if not 0 < 2 * ramp <= duration:
        raise ValueError('need 0 < 2·ramp ≤ duration')

    def envelope(t: float) -> float:
        if t <= 0.0 or t >= duration:
            return 0.0
        if t < ramp:
            return float(np.sin(0.5 * np.pi * t / ramp) ** 2)
        if t > duration - ramp:
            return float(np.sin(0.5 * np.pi * (duration - t) / ramp) ** 2)
        return 1.0
    return envelope

barriers

Gaussian2DBarrier(V0: float, width: float, x0: float = 0.0, y0: float = 0.0)

Bases: Potential

Isotropic 2D Gaussian barrier centred at (x0, y0):

V(x, y) = V0 · exp(−((x−x0)² + (y−y0)²) / width²)

Unlike the 1D GaussianBarrier, this is a true radially-symmetric hill that diffracts wave packets in both transverse directions.

Parameters:

Name Type Description Default
V0 float

Barrier height (energy units).

required
width float

Gaussian width parameter (same units as x, y).

required
x0 float

x-coordinate of the barrier centre. Default 0.0.

0.0
y0 float

y-coordinate of the barrier centre. Default 0.0.

0.0
Source code in quantumlab/potentials/barriers.py
def __init__(self, V0: float, width: float, x0: float = 0.0, y0: float = 0.0):
    self.V0 = V0
    self.width = width
    self.x0 = x0
    self.y0 = y0

Gaussian3DBarrier(V0: float, width: float, x0: float = 0.0, y0: float = 0.0, z0: float = 0.0)

Bases: Potential

Isotropic 3D spherical Gaussian barrier centred at (x0, y0, z0):

V(x, y, z) = V0 · exp(−((x−x0)² + (y−y0)² + (z−z0)²) / width²)

Parameters:

Name Type Description Default
V0 float

Barrier height.

required
width float

Gaussian width (same units as x, y, z).

required
x0 float

Barrier centre coordinates. Default 0.0.

0.0
y0 float

Barrier centre coordinates. Default 0.0.

0.0
z0 float

Barrier centre coordinates. Default 0.0.

0.0
Source code in quantumlab/potentials/barriers.py
def __init__(self, V0: float, width: float,
             x0: float = 0.0, y0: float = 0.0, z0: float = 0.0):
    self.V0 = V0
    self.width = width
    self.x0 = x0
    self.y0 = y0
    self.z0 = z0

wells

oscillator

periodic

disorder

custom