#!/usr/bin/env python3
"""
PROVENANCE: PROOF

Computation 189 -- The photoelectric cross-section and rate from the derived vertex:
the calculation comp 166 left, done
====================================================================================
comp 165 landed the kinematic half of the photoelectric effect and comp 166 showed the
interaction vertex's FORM is forced by gauge covariance, -e (psibar gamma^mu psi) A_mu,
the vector current and nothing else.  What both left was "the application": a
cross-section and a rate, "ordinary quantum electrodynamics once the form and the
strength are in hand".  This file does that application, so the photoelectric effect
is no longer a half.

WHAT IS COMPUTED, in order
--------------------------
  1.  THE CROSS-SECTION FROM THE VERTEX, symbolically.  In the non-relativistic
      reduction the vector vertex is (e/m) A . p: the current couples to the electron's
      MOMENTUM.  Fermi's golden rule with a hydrogenic 1s initial state, a plane-wave
      final state and the dipole limit of A gives, in atomic units (hbar = m = e = 1,
      lengths in a_0),

          sigma_B(k) = (2^8 pi / 3) alpha k^3 / (1 + k^2)^5,     hbar omega = (1 + k^2)/2,

      the angular integral and the algebra done by sympy, not by hand.  At high energy
      this is (2^8 pi / 3) alpha a_0^2 (I_H / hbar omega)^{7/2}, the Born photoionisation
      law, whose hbar omega^{-7/2} fall-off is the textbook result.

  2.  THE COULOMB FINAL STATE, and why it matters near threshold.  A plane wave ignores
      the ion the electron leaves behind.  The exact non-relativistic dipole result with
      Coulomb continuum waves (Stobbe; Bethe and Salpeter sec. 71) is

          sigma_S(k) = (2^9 pi^2 / 3) alpha a_0^2 (I_H/hbar omega)^4
                        exp(-4 eta arccot eta) / (1 - exp(-2 pi eta)),   eta = 1/k.

      Checked here: sigma_S / sigma_B -> 1 as k -> infinity (the Coulomb distortion
      disappears at high energy, so the vertex-derived law is the high-energy limit of
      the exact one), and at threshold sigma_S -> (2^9 pi^2/3) alpha a_0^2 e^{-4}
      = 6.30 Mb for hydrogen, where the Born form vanishes as k^3.  The vertex fixes the
      law; the Coulomb wave supplies the threshold.

  3.  THE AXIAL ALTERNATIVE GIVES NO PHOTOEFFECT AT THIS ORDER, which is a third reason
      the derived form is the right one.  The non-relativistic reduction of the axial
      vertex psibar gamma^i gamma^5 psi A_i is the spin operator sigma . A acting on the
      large components, computed here from the explicit Dirac matrices: no derivative,
      no orbital operator.  A pure spin operator has matrix element <k| 1s> <chi'|sigma|chi>
      between exact eigenstates of the same atomic Hamiltonian, and <k|1s> = 0 by
      orthogonality.  So the vector vertex is not only what gauge covariance forces
      (comp 166); it is what the photoelectric effect needs.

  4.  THE RATE, AND WHY ABSORPTION IS PROMPT.  R = sigma Phi with Phi = I / hbar omega
      the photon flux.  At 1 W/m^2 and 20 eV on hydrogen, sigma = 2.2 Mb and
      R = 6.9e-5 per second per atom, a mean wait of four hours PER ATOM.  There is no
      delay in the emission itself: the electron receives one quantum, and the waiting
      time is exponentially distributed with no accumulation.  Over the 1e15 atoms of a
      square centimetre of cathode the first emission comes at 1/(N R) = 1.4e-11 s,
      two orders inside the 3e-9 s Lawrence--Beams bound.  The classical field has to
      ACCUMULATE a work function through an atomic cross-section: 248 s per atom at the
      same intensity, eleven orders outside the bound.  comp 165's ten orders was the
      same contrast with rougher inputs.

  5.  THE SCOPE, stated once.  The strength alpha is taken as measured: \\S sec:vertex
      says the electromagnetic coupling is not fixed anywhere in the book, and this file
      does not fix it.  What is derived is the law's form and its proportionality to
      alpha; what is inherited is alpha's value.

A "pass": the sympy angular integral equal to 4 pi/3; the symbolic cross-section
reducing to (2^8 pi/3) alpha k^3/(1+k^2)^5 exactly; its high-energy limit equal to the
Born law to one part in a thousand at k = 100; the Stobbe/Born ratio within 4% of 1 at
k = 100 and the Stobbe threshold within 0.01 Mb of 6.30 Mb; gamma^0 gamma^i gamma^5
block-diagonal with sigma^i in the upper block for i = 1, 2, 3; the first-emission time
below the Lawrence--Beams bound and the classical accumulation time above it.
"""
import math
import sympy as sp

alpha = 1 / 137.035999
a0 = 5.29177210903e-11       # m
Mb = 1e-22                   # m^2
I_H = 13.605693              # eV
eV = 1.602176634e-19

# --------------------------------------------------------------------------- part 1
def born_cross_section_symbolic():
    """Golden rule, velocity form, hydrogenic 1s -> plane wave, dipole limit, atomic units.

    sigma = (4 pi^2 alpha / omega) int dOmega k |<k| eps.p |1s>|^2,  rho(E) = k dOmega,
    <k| eps.p |1s> = (eps.k) phi(k) with phi(k) = (2 sqrt 2 / pi) / (1 + k^2)^2 normalised
    to int |phi|^2 d^3k = 1, omega = (1 + k^2)/2.
    """
    k, theta, phi_az, al = sp.symbols("k theta phi alpha", positive=True)
    phi_k = 2 * sp.sqrt(2) / sp.pi / (1 + k**2)**2
    ang = sp.integrate(sp.integrate(sp.cos(theta)**2 * sp.sin(theta), (theta, 0, sp.pi)), (phi_az, 0, 2 * sp.pi))
    omega = (1 + k**2) / 2
    sigma = sp.simplify(4 * sp.pi**2 * al / omega * k * k**2 * phi_k**2 * ang)
    target = sp.Rational(256, 3) * sp.pi * al * k**3 / (1 + k**2)**5
    return ang, sigma, sp.simplify(sigma - target) == 0

def sigma_born(k):
    return (256 * math.pi / 3) * alpha * k**3 / (1 + k**2)**5          # units of a_0^2

def sigma_born_asymptotic(k):
    x = 1 / (1 + k**2)                                                 # I_H / hbar omega
    return (256 * math.pi / 3) * alpha * x**3.5

# --------------------------------------------------------------------------- part 2
def sigma_stobbe(k):
    eta = 1 / k
    x = 1 / (1 + k**2)
    return (512 * math.pi**2 / 3) * alpha * x**4 * math.exp(-4 * eta * math.atan(k)) / (1 - math.exp(-2 * math.pi * eta))

# --------------------------------------------------------------------------- part 3
def axial_reduction():
    """gamma^0 gamma^i gamma^5 in the Dirac representation: block-diagonal, sigma^i in both
    blocks, so the axial vertex reduces to sigma . A on the large components."""
    s1 = sp.Matrix([[0, 1], [1, 0]]); s2 = sp.Matrix([[0, -sp.I], [sp.I, 0]]); s3 = sp.Matrix([[1, 0], [0, -1]])
    Z = sp.zeros(2); I2 = sp.eye(2)
    g0 = sp.BlockMatrix([[I2, Z], [Z, -I2]]).as_explicit()
    g5 = sp.BlockMatrix([[Z, I2], [I2, Z]]).as_explicit()
    ok = True
    for s in (s1, s2, s3):
        gi = sp.BlockMatrix([[Z, s], [-s, Z]]).as_explicit()
        M = g0 * gi * g5
        upper = M[:2, :2]; off1 = M[:2, 2:]; off2 = M[2:, :2]
        ok = ok and (upper == s) and (off1 == Z) and (off2 == Z)
    return ok

# --------------------------------------------------------------------------- part 4
def rates():
    hw = 20.0                                   # eV
    k = math.sqrt(hw / I_H - 1)
    sigma = sigma_stobbe(k) * a0**2             # m^2
    I = 1.0                                     # W/m^2
    flux = I / (hw * eV)
    R = sigma * flux
    N = 1e15                                    # atoms per cm^2 of cathode, order of magnitude
    t_first = 1 / (N * R)
    t_classical = I_H * eV / (I * math.pi * a0**2)
    return hw, sigma, flux, R, 1 / R, t_first, t_classical

def main():
    print("=" * 100)
    print("  Computation 189 -- the photoelectric cross-section and rate from the derived vertex")
    print("=" * 100)
    checks = []

    ang, sigma_sym, exact = born_cross_section_symbolic()
    print("\n  (1) THE CROSS-SECTION FROM THE VECTOR VERTEX, symbolically (atomic units)")
    print(f"      angular integral int cos^2 dOmega = {ang}")
    print(f"      sigma_B(k) = {sigma_sym}")
    print(f"      equals (2^8 pi/3) alpha k^3/(1+k^2)^5 exactly: {exact}")
    checks.append(("angular integral equals 4 pi/3", sp.simplify(ang - sp.Rational(4, 3) * sp.pi) == 0))
    checks.append(("symbolic cross-section equals (2^8 pi/3) alpha k^3/(1+k^2)^5", exact))
    r100 = sigma_born(100) / sigma_born_asymptotic(100)
    print(f"      high-energy limit: sigma_B / [(2^8 pi/3) alpha (I_H/hw)^{{7/2}}] at k = 100 = {r100:.5f}")
    checks.append(("Born form reaches the (I_H/hw)^{7/2} law to 1e-3 at k = 100", abs(r100 - 1) < 1e-3))

    print("\n  (2) THE COULOMB FINAL STATE: Stobbe's exact dipole result against the vertex law")
    print(f"      {'k':>6} {'hw/eV':>10} {'Born/Mb':>12} {'Stobbe/Mb':>12} {'Stobbe/Born':>12}")
    for k in (0.1, 1.0, 3.0, 10.0, 30.0, 100.0):
        hw = I_H * (1 + k * k)
        print(f"      {k:6.1f} {hw:10.1f} {sigma_born(k)*a0**2/Mb:12.4g} {sigma_stobbe(k)*a0**2/Mb:12.4g} {sigma_stobbe(k)/sigma_born(k):12.4f}")
    ratio100 = sigma_stobbe(100.0) / sigma_born(100.0)
    thr = (512 * math.pi**2 / 3) * alpha * math.exp(-4) * a0**2 / Mb
    print(f"      Stobbe/Born -> 1: {ratio100:.4f} at k = 100 (the Coulomb factor dies as 1/k)")
    print(f"      threshold: sigma_S(k -> 0) = (2^9 pi^2/3) alpha a_0^2 e^-4 = {thr:.3f} Mb; the Born form vanishes as k^3")
    checks.append(("Stobbe/Born within 4% of 1 at k = 100", abs(ratio100 - 1) < 0.04))
    checks.append(("Stobbe threshold within 0.01 Mb of 6.30 Mb", abs(thr - 6.30) < 0.01))

    print("\n  (3) THE AXIAL VERTEX REDUCES TO sigma . A, A PURE SPIN OPERATOR")
    ok_axial = axial_reduction()
    print(f"      gamma^0 gamma^i gamma^5 block-diagonal with sigma^i in the upper block, i = 1,2,3: {ok_axial}")
    print("      <k, chi'| sigma . A |1s, chi> = <k|1s> <chi'|sigma . A|chi> = 0 by orthogonality of exact")
    print("      eigenstates: no photoeffect at leading order from the axial form.")
    checks.append(("axial vertex reduces to the spin operator (no orbital part)", ok_axial))

    print("\n  (4) THE RATE, AND WHY ABSORPTION IS PROMPT  (hydrogen, 20 eV, 1 W/m^2)")
    hw, sigma, flux, R, wait, t_first, t_cl = rates()
    print(f"      sigma = {sigma/Mb:.2f} Mb   photon flux = {flux:.3e} m^-2 s^-1   R = {R:.3e} s^-1 per atom   mean wait = {wait:.2e} s")
    print(f"      first emission over 1e15 atoms: {t_first:.2e} s   Lawrence--Beams bound: 3e-9 s")
    print(f"      classical accumulation of I_H through pi a_0^2: {t_cl:.0f} s")
    checks.append(("first-emission time below the Lawrence--Beams bound", t_first < 3e-9))
    checks.append(("classical accumulation time above the bound", t_cl > 3e-9))

    print("\n  (5) SCOPE: alpha is taken as measured; the book does not fix the coupling (sec:vertex).")
    print("\n" + "=" * 100)
    for name, ok in checks:
        print(f"  {'ok  ' if ok else 'FAIL'}  {name}")
    print("=" * 100)
    print("  PASS" if all(ok for _, ok in checks) else "  FAIL")

if __name__ == "__main__":
    main()
