PT chemistry / quantitative validation
Ionization energies derived by PT
The periodic table gives the skeleton. Ionization energy tests whether that skeleton can produce measurable numbers: how much energy does it take to remove the first electron from an atom?
This page shows two levels: the geometric engine, readable and directly tied to shell polygons, then the full atomic engine adding PT screening and relaxation corrections, including the jj2 spinor correction, superheavy CPR, and the continuous CPR branch projected on the s/p/d/f channels.
PT vs references, Z = 1–118
The plotted references are rounded to the millielectronvolt.
Order of magnitude from rounding alone, from 25 eV to 5 eV.
Error bars would be sub-pixel here; superheavy references remain compiled/evaluated values.
MAE for Z = 1–86, NIST references.
MAE for Z = 1–103, measured references.
Global MAE for Z = 1–118 after CPR-continuum.
MAE for Z = 104–118, threshold branch jj2 + CPR.
Calibration comes from the PT chain, not from a chemistry fit.
From shell structure to measured value
Ionization energy is not just nuclear charge. It depends on the outer shell, inner-electron screening, period closures, half-fillings, and fine corrections moving the d and f blocks.
In PT, these contributions assemble into a screening action $S(Z)$. The nucleus attracts as $Z$, but the external electron sees an effective charge $Z_{eff}$ projected by period geometry.
Scale
The Rydberg is not injected as a free constant: it descends from $m_e$, $\alpha_{EM}$, and the PT cascade.
$Ry = m_e\alpha_{EM}^2/2$
Screening
The bare nuclear charge is reduced by a screening action built from shells, polygons, and PT corrections.
$Z_{eff}=Ze^{-S(Z)}$
Ionization
The removal energy then follows a Rydberg-like law, modulated by the period and by the ejection amplitude of the active channel.
$IE=Ry\,(Z_{eff}/per)^2\,A_{ej}$
Resonance
The continuous branch adds a $(Z\alpha)^2$ envelope projected by the s/p/d/f geometric vertices; the threshold branch remains active for superheavy elements.
$\Delta S=\Delta S_{jj2}+\Delta S_{CPR}+\Delta S_{cont}$
Checkpoints a chemist recognizes
full engine `IE_eV`| Z | Element | Block | Ref. eV | PT eV | Error |
|---|---|---|---|---|---|
| 1 | H | s | 13.598 | 13.598 | -0.003% |
| 2 | He | s | 24.587 | 24.593 | +0.024% |
| 3 | Li | s | 5.392 | 5.393 | +0.024% |
| 7 | N | p | 14.534 | 14.533 | -0.005% |
| 8 | O | p | 13.618 | 13.619 | +0.005% |
| 10 | Ne | p | 21.565 | 21.564 | -0.003% |
| 11 | Na | s | 5.139 | 5.137 | -0.040% |
| 18 | Ar | p | 15.760 | 15.760 | -0.002% |
| 24 | Cr | d | 6.767 | 6.764 | -0.046% |
| 29 | Cu | d | 7.726 | 7.727 | +0.014% |
| 36 | Kr | p | 14.000 | 14.006 | +0.044% |
| 55 | Cs | s | 3.894 | 3.892 | -0.048% |
| 61 | Pm | f | 5.582 | 5.568 | -0.247% |
| 71 | Lu | d | 5.426 | 5.433 | +0.131% |
| 72 | Hf | d | 6.825 | 6.814 | -0.154% |
| 83 | Bi | p | 7.286 | 7.290 | +0.057% |
| 86 | Rn | p | 10.749 | 10.753 | +0.041% |
| 103 | Lr | d | 5.510 | 5.517 | +0.127% |
| 106 | Sg | d | 7.850 | 7.843 | -0.085% |
| 111 | Rg | d | 10.560 | 10.536 | -0.230% |
| 116 | Lv | p | 6.878 | 6.876 | -0.029% |
| 118 | Og | p | 8.888 | 8.879 | -0.106% |
What the curve has to get right
An ionization curve is full of discontinuities. Alkali metals drop, noble gases rise, half-fillings create bumps, and d/f transitions do not behave like a simple $Z^2$ law.
That is why this test matters: PT must follow the chemical shape, not merely reproduce an average.
Attached script: public/scripts-source/ptc/ie_geo.py
94 lines, copied from the geometric PTC engine `ie_geo.py`. Attached proofs: public/scripts-source/ptc/PT_IE_SUPERHEAVY_JJ2_DERIVATION.md; public/scripts-source/ptc/PT_IE_CONTRACTION_PENETRATION_RESONANCE.md; public/scripts-source/ptc/PT_IE_CPR_CONTINUUM.md.
"""ie_geo.py — Ionization Energy from polygon geometry.
Derives IE from the geometric shell operator (ShellPolygon / AtomicShell)
without calling the full atom.py engine. Uses PT screening_action for
the effective charge, then modulates by the ejection amplitude of the
active polygon.
Zero adjustable parameters.
March 2026 — Persistence Theory
"""
from __future__ import annotations
import math
from ptc.constants import RY
from ptc.periodic import period
from ptc.shell_polygon import build_atomic_shell
def IE_geo_eV(Z: int) -> float:
"""Ionization energy (eV) from polygon geometry.
IE = Ry × (Z_eff / per)² × ejection
The ejection_amplitude now contains the unified PT formula
(insight #30: I_Fisher - I_GFT), absorbing the former 2-loop
self-energy correction. No separate 2-loop term needed.
Parameters
----------
Z:
Atomic number (1–118).
Returns
-------
float
Estimated first ionization energy in eV.
"""
from ptc.atom import screening_action # lazy import for Python 3.9 compat
shell = build_atomic_shell(Z)
per = period(Z)
S = screening_action(Z)
Z_eff = Z * math.exp(-S)
ie_base = RY * (Z_eff / per) ** 2
# Ejection from active polygon — unified (DC + pairing + I_Fisher - I_GFT).
ej = shell.active_polygon.ejection_amplitude()
return ie_base * ej
def benchmark_ie_geo() -> dict:
"""Benchmark IE_geo_eV against NIST first ionization energies.
Returns
-------
dict with keys:
count : int — number of elements benchmarked
mae_percent : float — mean absolute error in percent
by_block : dict[str, float] — MAE per block (s, p, d, f)
rows : list[dict] — per-element details
"""
from ptc.data.experimental import IE_NIST
from ptc.periodic import block_of
rows = []
block_errors: dict[str, list[float]] = {}
for Z, ie_ref in sorted(IE_NIST.items()):
if ie_ref <= 0:
continue
ie_calc = IE_geo_eV(Z)
err_pct = abs(ie_calc - ie_ref) / ie_ref * 100.0
blk = block_of(Z)
block_errors.setdefault(blk, []).append(err_pct)
rows.append({
"Z": Z,
"block": blk,
"ie_ref": ie_ref,
"ie_calc": ie_calc,
"err_pct": err_pct,
})
mae = sum(r["err_pct"] for r in rows) / len(rows) if rows else 0.0
by_block = {blk: sum(errs) / len(errs) for blk, errs in block_errors.items()}
return {
"count": len(rows),
"mae_percent": mae,
"by_block": by_block,
"rows": rows,
}