qscat.ecs

The exterior-complex-scaling coordinate map — the single source of the z(x) transform used by every complex tail — and the resonance-pole matchers built on angle stability.

ecs_map

Exterior-complex-scaling coordinate map.

find_resonance_pole

Match the angle-stable resonance pole between two eigenvalue spectra.

match_angle_stable

Every angle-stable eigenvalue shared by two ECS spectra, not just one.

Exterior Complex Scaling (ECS) utilities.

The ECS contour maps the real radial coordinate onto a path that runs straight out to a pivot R0 and then bends into the complex plane at a fixed angle theta. Rotating the outgoing (continuum) coordinate this way turns divergent scattering states into decaying ones and exposes resonance poles, while leaving bound-state energies unchanged (Rescigno & McCurdy, Phys. Rev. A 62, 032706 (2000); see docs/physics/femdvr-ecs.md).

This module is the single source of that transform: qscat.dvr.grid uses ecs_map to place its complex-tail quadrature points.

It also carries find_resonance_pole, the general two-spectrum resonance-pole matcher promoted from the N2 resonance project (projects/n2_resonance/pole.py) – see docs/physics/n2-resonance.md.

match_angle_stable is its multi-state sibling: same acceptance criterion, but it returns EVERY angle-stable eigenvalue in a window (with the indices needed to recover the eigenvectors), which is what a level spectrum needs.

qscat.ecs.ecs_map(x, R0, theta_deg)[source]

Exterior-complex-scaling coordinate map.

z(x) = x for x <= R0, and z(x) = R0 + (x - R0) * exp(i*theta) for x > R0. x may be a scalar or array; theta_deg is in degrees.

Parameters:
Return type:

NDArray[complexfloating]

qscat.ecs.find_resonance_pole(eigs_a, eigs_b, window)[source]

Match the angle-stable resonance pole between two eigenvalue spectra.

eigs_a/eigs_b are complex eigenvalue arrays of the same Hamiltonian computed at two different ECS rotation angles (or otherwise perturbed in a way that moves the discretized continuum but not a true pole). Both spectra are restricted to window = (re_lo, re_hi, im_lo, im_hi), and the pair (ea, eb) – one eigenvalue from each, restricted set – with the smallest |ea - eb| is returned as the matched pole: E_pole = 0.5*(ea+eb), residual = |ea-eb|. A small residual (<< the resonance width) is the signature of a genuine angle-stable pole; discretized continuum eigenvalues rotate with the angle and do not match this closely.

Raises ValueError if window contains no eigenvalues in either input spectrum (window too tight, or grid too coarse to resolve the pole).

Parameters:
Return type:

tuple[complex, float]

qscat.ecs.match_angle_stable(eigs_a, eigs_b, window, *, rel_tol=0.0001, atol=1e-08)[source]

Every angle-stable eigenvalue shared by two ECS spectra, not just one.

The multi-state generalization of find_resonance_pole: eigs_a/eigs_b are complex eigenvalue arrays of the same Hamiltonian at two different ECS rotation angles. An eigenvalue of eigs_a inside window is ACCEPTED when its nearest eigs_b partner satisfies

|E_a - E_b| < max(rel_tol * |E_a|, atol)

– eMoScat’s DiscreteStates criterion, vectorized. Discretized continuum eigenvalues rotate with the angle and fail it; bound and resonance states do not. Returns (energies, residuals, indices), ascending in Re E: energies the midpoints (E_a + E_b)/2 (matching find_resonance_pole’s convention), residuals the |E_a - E_b| per accepted state, and indices the positions in the ORIGINAL eigs_a – so a caller holding the grid-a eigenvectors can pull the matching columns straight out.

An empty result is a normal outcome (no stable state in window), NOT an error. ValueError is raised only when window catches nothing at all in one of the two spectra, mirroring find_resonance_pole.

Parameters:
Return type:

tuple[NDArray[complex128], NDArray[float64], NDArray[int64]]