qscat.model

Everything tied to a specific molecule: the ResonanceModel protocol that qscat.core depends on, the shared neutral and ionic potential forms, the potential factory’s flexible form (FlexibleDiatomicModel, with SmoothR sigmoid or long-range-correct TailR coefficient functions, embedding the published models exactly via from_diatomic), and the molecule registry — O2 being the first entry that is a fit rather than a published parameter set. Adding a molecule is a registry entry plus validation, never solver code.

ResonanceModel

Structural type: anything with this shape is a model qscat.core can drive, regardless of functional form.

DiatomicResonanceModel

The shared Morse + sigmoid + Gaussian-in-r resonance-model form, implementing ResonanceModel.

IonicResonanceModel

The H2+ Morse + sigma-capture + Coulomb-tail resonance-model form, implementing ResonanceModel.

FlexibleDiatomicModel

EMO neutral curve + Gaussian well with lam(R), alpha(R) + optional shell.

SmoothR

f(R) = f_inf + f_0 / (1 + exp(f_1 (R - R_f))) * (1 + sum_i coeffs[i] y_p(R)^(i+1)).

TailR

f(R) = f_inf + (1 - y_q(R)) * sum_k coeffs[k] y_p(R)^k.

from_diatomic

Embed a published DiatomicResonanceModel exactly (to round-off).

N2

The shared Morse + sigmoid + Gaussian-in-r resonance-model form, implementing ResonanceModel.

NO

The shared Morse + sigmoid + Gaussian-in-r resonance-model form, implementing ResonanceModel.

F2

The shared Morse + sigmoid + Gaussian-in-r resonance-model form, implementing ResonanceModel.

H2P

The H2+ Morse + sigma-capture + Coulomb-tail resonance-model form, implementing ResonanceModel.

O2

EMO neutral curve + Gaussian well with lam(R), alpha(R) + optional shell.

O2_SO12

EMO neutral curve + Gaussian well with lam(R), alpha(R) + optional shell.

O2_SO32

EMO neutral curve + Gaussian well with lam(R), alpha(R) + optional shell.

The model layer: the ResonanceModel protocol, the shared diatomic resonance-model form, the H2+ ionic resonance-model form, and the per-molecule parameter registry.

Public API:
  • ResonanceModel – the structural protocol qscat.core’s solvers depend on (never a concrete class below).

  • DiatomicResonanceModel – the shared Morse + sigmoid + Gaussian-in-r form (N2/NO/F2 differ only in parameters).

  • IonicResonanceModel – the H2+ Morse + sigma-capture + Coulomb-tail form.

  • FlexibleDiatomicModel (+ SmoothR/TailR) – the potential factory’s fitted form: EMO neutral + Gaussian well with lam(R), alpha(R) and an optional shell; embeds the DiatomicResonanceModel`s exactly (`from_diatomic) – see docs/physics/potential-factory.md.

  • N2, NO, F2, H2P, O2 – the registry instances (O2 is the first FITTED model, not a published parameter set), plus O2_SO12 / O2_SO32, its two spin-orbit components (statistical factor 1/3 each).

Adding a molecule means adding a registry entry (parameters) plus its validation – never solver code, which lives model-free in qscat.core. See docs/physics/qscat-core-scattering.md.

class qscat.model.DiatomicResonanceModel(mu, ell, D0, alpha0, R0, lambda_inf, lambda_1, R_lambda, lambda_c, R_c, alpha_c, charge=0)[source]

The shared Morse + sigmoid + Gaussian-in-r resonance-model form, implementing ResonanceModel. Carries no grid state – purely the potential-surface parameters; qscat.model.library holds the per-molecule instances.

Parameters:
v0(R)[source]

Neutral Morse potential (Hartree). Minimum -D0 at R0.

R is converted to complex128, never a real dtype: it may be complex (ECS-rotated tail points), and coercing to real would silently discard Im(R) and corrupt the analytic continuation the exterior-complex-scaling method relies on.

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

lam(R)[source]

Sigmoid interaction strength; lam(R_c) == lambda_c.

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

v_int(r, R)[source]

Electron-molecule interaction potential (Hartree).

r is converted to complex128, never a real dtype: it may be complex (ECS-rotated tail points), and coercing to real would silently discard Im(r) and corrupt the analytic continuation the exterior-complex-scaling method relies on.

Parameters:
  • r (ArrayLike)

  • R (ArrayLike)

Return type:

NDArray[complex128]

surface(r, R)[source]

The full surface v0(R) + ell(ell+1)/(2 r^2) + v_int(r, R).

Must not coerce to a real dtype: r/R are complex on the ECS tails.

Parameters:
  • r (ArrayLike)

  • R (ArrayLike)

Return type:

NDArray[complex128]

hamiltonian(tgrid)[source]

H_2D on tgrid (axis 0 = electronic r, axis 1 = nuclear R).

Parameters:

tgrid (TensorGrid)

Return type:

csr_matrix

interaction_diag(tgrid)[source]

V_int evaluated on the tensor grid, flattened (C order).

Parameters:

tgrid (TensorGrid)

Return type:

NDArray[complex128]

class qscat.model.FlexibleDiatomicModel(mu, ell, D_e, R_e, betas, p, lam, alpha, shell, alpha_b, r_b, charge=0)[source]

EMO neutral curve + Gaussian well with lam(R), alpha(R) + optional shell.

Parameters:
beta_R(R)[source]

EMO exponent beta(R) = sum_i betas[i] y_p(R)^i, a polynomial in Le Roy’s y_p.

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

v0(R)[source]

EMO: D_e [ (1 - exp(-beta(R)(R - R_e)))^2 - 1 ], minimum -D_e at R_e, 0 at infinity.

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

lam_R(R)[source]

Well depth lam(R) of the interaction (SmoothR or TailR).

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

alpha_R(R)[source]

Well width parameter alpha(R) of the interaction.

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

shell_R(R)[source]

Repulsive-shell amplitude at R; zero everywhere when there is no shell.

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

v_int(r, R)[source]

Electron–molecule interaction -lam(R) e^{-alpha(R) r^2} plus the optional shell shell(R) e^{-alpha_b (r - r_b)^2} (the ResonanceModel contract).

Parameters:
  • r (ArrayLike)

  • R (ArrayLike)

Return type:

NDArray[complex128]

surface(r, R)[source]

Full 2-D potential v0(R) + l(l+1)/(2 r^2) + v_int(r, R).

Parameters:
  • r (ArrayLike)

  • R (ArrayLike)

Return type:

NDArray[complex128]

hamiltonian(tgrid)[source]

Sparse 2-D Hamiltonian on tgrid (electron mass 1, nuclear mass mu).

Parameters:

tgrid (TensorGrid)

Return type:

csr_matrix

interaction_diag(tgrid)[source]

v_int sampled on tgrid’s nodes, in the grid’s flattened order.

Parameters:

tgrid (TensorGrid)

Return type:

NDArray[complex128]

with_shell(shell, alpha_b, r_b)[source]

A copy carrying the repulsive shell shell(R) e^{-alpha_b (r - r_b)^2}.

Parameters:
Return type:

FlexibleDiatomicModel

class qscat.model.IonicResonanceModel(mu, ell, charge, V0, R0, alpha, a1, a2, a3, a4, max_nuclear_ecs_angle_deg=22.5)[source]

The H2+ Morse + sigma-capture + Coulomb-tail resonance-model form, implementing ResonanceModel. Carries no grid state – purely the potential-surface parameters; qscat.model.library holds the H2+ instance (H2P).

Parameters:
v0(R)[source]

Ion-core Morse potential (Hartree). Minimum -V0 at R0.

R is converted to complex128, never a real dtype: it may be complex (ECS-rotated tail points), and coercing to real would silently discard Im(R) and corrupt the analytic continuation the exterior-complex-scaling method relies on.

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

v_int(r, R)[source]

Sigma-capture electron-molecule interaction potential (Hartree).

v_int(r,R) = -a1 * (1 - tanh Q(R)) * S(R) * (exp(-r^2/3) / r), Q(R) = (a2 - R - a3 R^4) / 7, S(R) = tanh(R/a4)^4.

r/R are converted to complex128, never a real dtype: they may be complex (ECS-rotated tail points), and coercing to real would silently discard Im(r)/Im(R) and corrupt the analytic continuation the exterior-complex-scaling method relies on.

Parameters:
  • r (ArrayLike)

  • R (ArrayLike)

Return type:

NDArray[complex128]

surface(r, R)[source]

The full surface v0(R) + v_int(r, R) + ell(ell+1)/(2 r^2) + charge/r – the charge/r term is the electron-core Coulomb tail (charge=-1 for H2+, the -1/r attraction).

Must not coerce to a real dtype: r/R are complex on the ECS tails.

Parameters:
  • r (ArrayLike)

  • R (ArrayLike)

Return type:

NDArray[complex128]

hamiltonian(tgrid)[source]

H_2D on tgrid (axis 0 = electronic r, axis 1 = nuclear R).

Parameters:

tgrid (TensorGrid)

Return type:

csr_matrix

interaction_diag(tgrid)[source]

V_int evaluated on the tensor grid, flattened (C order).

Parameters:

tgrid (TensorGrid)

Return type:

NDArray[complex128]

class qscat.model.ResonanceModel(*args, **kwargs)[source]

Structural type: anything with this shape is a model qscat.core can drive, regardless of functional form.

mu/ell/charge are per-molecule constants (nuclear reduced mass, fixed partial wave, Coulomb charge of the residual channel – 0 for a neutral target, -1 for a cation like H2+). v0/v_int/surface are the potential-energy surface, evaluated pointwise (scalars or broadcastable arrays; r/R may be COMPLEX on an exterior-complex-scaling tail, and an implementation must not coerce them to a real dtype). hamiltonian/interaction_diag assemble those surfaces onto a qscat.dvr.TensorGrid: the sparse H_2D and the flattened diag(V_int) a time-dependent free-reference subtracts off H_2D.

mu/ell are declared as READ-ONLY properties (not plain mutable attributes) so that a frozen dataclass like DiatomicResonanceModel – whose fields are read-only by construction – satisfies this protocol structurally under mypy. A plain mu: float annotation here would require a SETTABLE attribute (mypy’s default for Protocol attributes), which a frozen dataclass field can never be; qscat.core.driven’s model: ResonanceModel parameter is the first real static consumer of this protocol (earlier code only used isinstance at runtime, which does not distinguish read-only from settable), so the mismatch stayed latent until a solver first took model: ResonanceModel statically.

property mu: float

Nuclear reduced mass (a.u.).

property ell: int

Resonance partial-wave angular momentum l.

property charge: int

The Coulomb charge z for the channel functions; 0 for neutral targets, -1 for a singly-charged cation like H2+.

v0(R)[source]

Neutral molecule’s own potential (Hartree); a CHANNEL potential – survives as r -> infinity.

Parameters:

R (ArrayLike)

Return type:

NDArray[complex128]

v_int(r, R)[source]

V_int(r, R) = -lambda(R) exp(-alpha_c r^2) – the perturbation ALONE, excluding v0(R) and the centrifugal term.

Parameters:
  • r (ArrayLike)

  • R (ArrayLike)

Return type:

NDArray[complex128]

surface(r, R)[source]

The full surface v0(R) + ell(ell+1)/(2 r^2) + v_int(r, R) that goes into hamiltonian.

Parameters:
  • r (ArrayLike)

  • R (ArrayLike)

Return type:

NDArray[complex128]

hamiltonian(tgrid)[source]

H_2D on tgrid (axis 0 electronic r, axis 1 nuclear R), sparse CSR, complex symmetric (never Hermitian) whenever an axis has an ECS tail.

Parameters:

tgrid (TensorGrid)

Return type:

csr_matrix

interaction_diag(tgrid)[source]

V_int evaluated on tgrid, flattened (C order) – what a time-dependent free-reference subtracts off hamiltonian(tgrid).

Parameters:

tgrid (TensorGrid)

Return type:

NDArray[complex128]

class qscat.model.SmoothR(f_inf, f_0, f_1, R_f, coeffs=(), R_e=2.0, p=3)[source]

f(R) = f_inf + f_0 / (1 + exp(f_1 (R - R_f))) * (1 + sum_i coeffs[i] y_p(R)^(i+1)).

With coeffs == () this is exactly Houfek’s sigmoid lambda_inf + lambda_0 / (1 + exp(lambda_1 (R - R_lambda))).

Parameters:
class qscat.model.TailR(f_inf, coeffs, R_e=2.0, p=3, q=4)[source]

f(R) = f_inf + (1 - y_q(R)) * sum_k coeffs[k] y_p(R)^k.

The long-range-correct alternative to SmoothR: every term carries the factor 1 - y_q(R) ~ 2 (R_e/R)^q as R -> inf, so f(inf) == f_inf EXACTLY and the approach is the power law R^-q – with q = 4 the ion–atom polarisation form the anion curve must follow (the polarisation_tail of target.py). Inside, P(y_p) is a plain polynomial in Le Roy’s bounded variable y_p in (-1, 1), so the fit is linear in coeffs and well conditioned – no sigmoid inflection to run off to large R, which is how the SmoothR form held -EA at one node and missed it by 0.2 eV beyond (measured on O2). SmoothR stays for the published models, whose lam(R) IS a sigmoid.

Parameters:
qscat.model.from_diatomic(model)[source]

Embed a published DiatomicResonanceModel exactly (to round-off).

Parameters:

model (DiatomicResonanceModel)

Return type:

FlexibleDiatomicModel