Source code for qscat.dvr.operators

"""Hamiltonian assembly + eigensolver helpers for the FEM-DVR-ECS grid.

See `docs/physics/femdvr-ecs.md`. H = T + diag(V) is complex-symmetric but non-Hermitian in
general (ECS elements), so we use the general eigensolver (np.linalg.eig,
c.f. ZGEEV) rather than a Hermitian one, and sort by ascending Re(E).
"""

from __future__ import annotations

from collections.abc import Callable

import numpy as np
import numpy.typing as npt

from .grid import FemDvrEcsGrid
from .kinetic import kinetic

__all__ = ["eigen", "hamiltonian"]

PotentialLike = Callable[[npt.NDArray[np.complex128]], npt.ArrayLike] | npt.ArrayLike


[docs] def hamiltonian(grid: FemDvrEcsGrid, V: PotentialLike, mass: float) -> npt.NDArray[np.complex128]: """Assemble H = T + diag(V) on the FEM-DVR basis (diagonal-potential approx).""" T = kinetic(grid, mass) raw_vals = V(grid.points) if callable(V) else V Vals = np.broadcast_to(np.asarray(raw_vals), (grid.n,)).astype(np.complex128) result: npt.NDArray[np.complex128] = T + np.diag(Vals) return result
[docs] def eigen( H: npt.NDArray[np.complex128], ) -> tuple[npt.NDArray[np.complex128], npt.NDArray[np.complex128]]: """Eigendecompose H, sorted by ascending Re(E). Eigenvectors carry numpy's `v†v=1` (Hermitian) normalization; for ECS c-product observables (resonance residues and widths), re-normalize to `vᵀv=1`. """ E, vecs = np.linalg.eig(H) # complex, non-Hermitian order = np.argsort(E.real) return E[order], vecs[:, order]