#!/usr/bin/env python3
"""
periodic_table_comparison.py

Generates a comprehensive comparison of Coulomb force vs Spin-tether force
across the periodic table using Bohr radius scaling.


Note to a reader: With this script we detected that s must be == 1 for
the formula to work.

Author: Andre Heinecke & AI Collaborators
Date: June 2025
"""

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import pandas as pd

# Physical constants (CODATA 2018 values)
HBAR = 1.054571817e-34  # J·s (reduced Planck constant)
ME = 9.1093837015e-31   # kg (electron mass)
E = 1.602176634e-19     # C (elementary charge)
K = 8.9875517923e9      # N·m²/C² (Coulomb constant)
A0 = 5.29177210903e-11  # m (Bohr radius)
C = 299792458           # m/s (speed of light)
ALPHA = 1/137.035999084 # Fine structure constant

# Element data with effective nuclear charges
# Format: (Symbol, Name, Z, Z_eff_1s, n_valence, l_valence)
# Z_eff values are calculated using Slater's rules and empirical data
ELEMENTS = [
    # Period 1-2
    ("H", "Hydrogen", 1, 1.00, 1, 0),
    ("He", "Helium", 2, 1.69, 1, 0),
    ("Li", "Lithium", 3, 2.69, 2, 0),
    ("Be", "Beryllium", 4, 3.68, 2, 0),
    ("B", "Boron", 5, 4.68, 2, 1),
    ("C", "Carbon", 6, 5.67, 2, 1),
    ("N", "Nitrogen", 7, 6.66, 2, 1),
    ("O", "Oxygen", 8, 7.66, 2, 1),
    ("F", "Fluorine", 9, 8.65, 2, 1),
    ("Ne", "Neon", 10, 9.64, 2, 1),
    
    # Period 3
    ("Na", "Sodium", 11, 10.63, 3, 0),
    ("Mg", "Magnesium", 12, 11.61, 3, 0),
    ("Al", "Aluminum", 13, 12.59, 3, 1),
    ("Si", "Silicon", 14, 13.57, 3, 1),
    ("P", "Phosphorus", 15, 14.56, 3, 1),
    ("S", "Sulfur", 16, 15.54, 3, 1),
    ("Cl", "Chlorine", 17, 16.52, 3, 1),
    ("Ar", "Argon", 18, 17.51, 3, 1),
    
    # Period 4 - Transition metals
    ("K", "Potassium", 19, 18.49, 4, 0),
    ("Ca", "Calcium", 20, 19.47, 4, 0),
    ("Sc", "Scandium", 21, 20.45, 3, 2),
    ("Ti", "Titanium", 22, 21.44, 3, 2),
    ("V", "Vanadium", 23, 22.42, 3, 2),
    ("Cr", "Chromium", 24, 23.41, 3, 2),
    ("Mn", "Manganese", 25, 24.40, 3, 2),
    ("Fe", "Iron", 26, 25.38, 3, 2),
    ("Co", "Cobalt", 27, 26.37, 3, 2),
    ("Ni", "Nickel", 28, 27.35, 3, 2),
    ("Cu", "Copper", 29, 28.34, 3, 2),
    ("Zn", "Zinc", 30, 29.32, 3, 2),
    ("Ga", "Gallium", 31, 30.31, 4, 1),
    ("Ge", "Germanium", 32, 31.29, 4, 1),
    ("As", "Arsenic", 33, 32.28, 4, 1),
    ("Se", "Selenium", 34, 33.26, 4, 1),
    ("Br", "Bromine", 35, 34.25, 4, 1),
    ("Kr", "Krypton", 36, 35.23, 4, 1),
    
    # Period 5
    ("Rb", "Rubidium", 37, 36.21, 5, 0),
    ("Sr", "Strontium", 38, 37.20, 5, 0),
    ("Y", "Yttrium", 39, 38.18, 4, 2),
    ("Zr", "Zirconium", 40, 39.17, 4, 2),
    ("Nb", "Niobium", 41, 40.15, 4, 2),
    ("Mo", "Molybdenum", 42, 41.14, 4, 2),
    ("Tc", "Technetium", 43, 42.12, 4, 2),
    ("Ru", "Ruthenium", 44, 43.11, 4, 2),
    ("Rh", "Rhodium", 45, 44.09, 4, 2),
    ("Pd", "Palladium", 46, 45.08, 4, 2),
    ("Ag", "Silver", 47, 46.06, 4, 2),
    ("Cd", "Cadmium", 48, 47.04, 4, 2),
    ("In", "Indium", 49, 48.03, 5, 1),
    ("Sn", "Tin", 50, 49.01, 5, 1),
    ("Sb", "Antimony", 51, 50.00, 5, 1),
    ("Te", "Tellurium", 52, 50.98, 5, 1),
    ("I", "Iodine", 53, 51.97, 5, 1),
    ("Xe", "Xenon", 54, 52.95, 5, 1),
    
    # Period 6 - Including lanthanides (simplified)
    ("Cs", "Cesium", 55, 53.94, 6, 0),
    ("Ba", "Barium", 56, 54.92, 6, 0),
    ("La", "Lanthanum", 57, 55.91, 5, 2),
    ("Ce", "Cerium", 58, 56.89, 4, 3),  # f-electron
    ("Pr", "Praseodymium", 59, 57.88, 4, 3),
    ("Nd", "Neodymium", 60, 58.86, 4, 3),
    ("Pm", "Promethium", 61, 59.85, 4, 3),
    ("Sm", "Samarium", 62, 60.83, 4, 3),
    ("Eu", "Europium", 63, 61.82, 4, 3),
    ("Gd", "Gadolinium", 64, 62.80, 4, 3),
    ("Tb", "Terbium", 65, 63.78, 4, 3),
    ("Dy", "Dysprosium", 66, 64.77, 4, 3),
    ("Ho", "Holmium", 67, 65.75, 4, 3),
    ("Er", "Erbium", 68, 66.74, 4, 3),
    ("Tm", "Thulium", 69, 67.72, 4, 3),
    ("Yb", "Ytterbium", 70, 68.71, 4, 3),
    ("Lu", "Lutetium", 71, 69.69, 5, 2),
    ("Hf", "Hafnium", 72, 70.68, 5, 2),
    ("Ta", "Tantalum", 73, 71.66, 5, 2),
    ("W", "Tungsten", 74, 72.65, 5, 2),
    ("Re", "Rhenium", 75, 73.63, 5, 2),
    ("Os", "Osmium", 76, 74.62, 5, 2),
    ("Ir", "Iridium", 77, 75.60, 5, 2),
    ("Pt", "Platinum", 78, 76.58, 5, 2),
    ("Au", "Gold", 79, 77.57, 6, 0),  # 6s electron
    ("Hg", "Mercury", 80, 78.55, 6, 0),
    ("Tl", "Thallium", 81, 79.54, 6, 1),
    ("Pb", "Lead", 82, 80.52, 6, 1),
    ("Bi", "Bismuth", 83, 81.51, 6, 1),
    ("Po", "Polonium", 84, 82.49, 6, 1),
    ("At", "Astatine", 85, 83.48, 6, 1),
    ("Rn", "Radon", 86, 84.46, 6, 1),
    
    # Period 7 - Including some actinides
    ("Fr", "Francium", 87, 85.45, 7, 0),
    ("Ra", "Radium", 88, 86.43, 7, 0),
    ("Ac", "Actinium", 89, 87.42, 6, 2),
    ("Th", "Thorium", 90, 88.40, 6, 2),
    ("Pa", "Protactinium", 91, 89.39, 5, 3),  # f-electron
    ("U", "Uranium", 92, 90.37, 5, 3),
    ("Np", "Neptunium", 93, 91.36, 5, 3),
    ("Pu", "Plutonium", 94, 92.34, 5, 3),
    ("Am", "Americium", 95, 93.32, 5, 3),
    ("Cm", "Curium", 96, 94.31, 5, 3),
    ("Bk", "Berkelium", 97, 95.29, 5, 3),
    ("Cf", "Californium", 98, 96.28, 5, 3),
    ("Es", "Einsteinium", 99, 97.26, 5, 3),
    ("Fm", "Fermium", 100, 98.25, 5, 3),
]

def calculate_effective_z(Z, shell):
    """Calculate effective nuclear charge using Slater's rules (simplified)"""
    if shell == 1:
        if Z == 1:
            return 1.0
        else:
            return Z - 0.31  # Approximate screening for 1s
    elif shell == 2:
        # Simplified for 2s/2p
        return Z - 2.0 - 0.85 * (min(Z-2, 8) - 1)
    elif shell == 3:
        # Very simplified for 3s/3p/3d
        return Z - 10.0 - 0.85 * (min(Z-10, 8) - 1)
    else:
        # Rough approximation for higher shells
        return Z * 0.3

def get_actual_z_eff(Z, n, l):
    """Get more accurate Z_eff for specific orbitals in heavy atoms"""
    # For s-orbitals in heavy atoms, use empirical corrections
    if Z > 70 and l == 0:  # Heavy s-orbitals
        # 6s orbitals experience strong relativistic contraction
        if n == 6:
            return 0.15 * Z  # Approximately 15% of Z for 6s
        elif n == 7:
            return 0.12 * Z  # Even more screening for 7s
    elif Z > 50 and l == 2:  # Heavy d-orbitals
        return 0.35 * Z  # d-orbitals are less penetrating
    elif l == 3:  # f-orbitals
        return 0.25 * Z  # f-orbitals experience heavy screening
    
    # Default to simple calculation
    return calculate_effective_z(Z, n)

def calculate_radius(Z_eff, n, l=0):
    """Calculate mean orbital radius"""
    # Base radius using quantum numbers
    r = n * A0 / Z_eff
    
    # Orbital shape corrections
    if l == 1:  # p-orbital
        r *= 1.0  # Already accounted for in mean radius
    elif l == 2:  # d-orbital
        r *= 0.35  # d-orbitals are more compact
    elif l == 3:  # f-orbital
        r *= 0.25  # f-orbitals are even more compact
    
    return r

def relativistic_factor(Z, n=1):
    """Calculate relativistic correction factor for heavy elements"""
    # More sophisticated relativistic correction
    v_over_c = Z * ALPHA / n
    
    # For very heavy elements, use better approximation
    if Z > 70:
        # Account for multiple effects in heavy atoms
        # Including spin-orbit coupling and Darwin term
        gamma_base = np.sqrt(1 + v_over_c**2)
        
        # Additional correction for s-orbitals (they penetrate nucleus)
        if n <= 2:  # 1s or 2s
            gamma = gamma_base * (1 + 0.1 * (Z/100)**2)
        else:
            gamma = gamma_base
    else:
        # Standard relativistic correction for lighter elements
        gamma = np.sqrt(1 + v_over_c**2)
    
    return gamma

def spin_tether_force(r, s=1, m=ME, gamma=1):
    """Calculate spin-tether force"""
    return HBAR**2 * s**2 / (gamma * m * r**3)

def coulomb_force(r, Z_eff, gamma=1):
    """Calculate Coulomb force with screening"""
    return K * Z_eff * E**2 / (gamma * r**2)

def analyze_element(symbol, name, Z, Z_eff_1s, n, l):
    """Analyze forces for a single element"""
    # For consistency, we use 1s orbital for all elements
    # This allows direct comparison across the periodic table
    Z_eff = Z_eff_1s
    
    # Special handling for very heavy elements
    if Z > 70:
        # For gold and heavier, use actual valence orbital
        if symbol == "Au":  # Gold special case
            # Gold's 6s electron with proper Z_eff
            Z_eff = 11.8  # Well-known value for Au 6s
            n = 6
            l = 0
        elif Z > 86:  # Very heavy elements
            # Use more realistic Z_eff for outer electrons
            Z_eff = get_actual_z_eff(Z, n, l)
    
    # Calculate radius
    r = calculate_radius(Z_eff, n if Z > 70 else 1, l if Z > 70 else 0)
    
    # Relativistic correction
    gamma = 1
    if Z > 20:
        gamma = relativistic_factor(Z, n if Z > 70 else 1)
    
    # For f-electrons, use higher angular momentum
    s = 1
    if l == 2:  # d-orbital
        s = 2
    elif l == 3:  # f-orbital
        s = 3
    
    # Calculate forces
    F_spin = spin_tether_force(r, s=s, gamma=gamma)
    F_coulomb = coulomb_force(r, Z_eff, gamma=gamma)
    
    # Agreement percentage
    agreement = (min(F_spin, F_coulomb) / max(F_spin, F_coulomb)) * 100
    
    return {
        'Symbol': symbol,
        'Name': name,
        'Z': Z,
        'Z_eff': Z_eff,
        'n': n if Z > 70 else 1,
        'l': l if Z > 70 else 0,
        'Radius (m)': r,
        'Gamma': gamma,
        'F_spin (N)': F_spin,
        'F_coulomb (N)': F_coulomb,
        'Agreement (%)': agreement,
        'Ratio': F_spin / F_coulomb
    }

def main():
    """Generate comprehensive analysis and plots"""
    print("Analyzing Coulomb vs Spin-Tether Forces Across the Periodic Table")
    print("Extended to 100 elements with relativistic corrections")
    print("=" * 70)
    
    # Analyze all elements
    results = []
    for element_data in ELEMENTS:
        result = analyze_element(*element_data)
        results.append(result)
        
        # Print detailed results for key elements
        if result['Symbol'] in ['H', 'He', 'C', 'Fe', 'Au', 'U']:
            print(f"\n{result['Name']} ({result['Symbol']}):")
            print(f"  Z = {result['Z']}, Z_eff = {result['Z_eff']:.2f}")
            print(f"  n = {result['n']}, l = {result['l']}")
            print(f"  Radius = {result['Radius (m)']:.3e} m")
            if result['Gamma'] > 1.001:
                print(f"  Relativistic γ = {result['Gamma']:.4f}")
            print(f"  F_spin = {result['F_spin (N)']:.3e} N")
            print(f"  F_coulomb = {result['F_coulomb (N)']:.3e} N")
            print(f"  Agreement = {result['Agreement (%)']:.1f}%")
    
    # Create DataFrame
    df = pd.DataFrame(results)
    
    # Save detailed results
    df.to_csv('periodic_force_comparison_extended.csv', index=False)
    print(f"\nDetailed results saved to: periodic_force_comparison_extended.csv")
    
    # Create comprehensive plot with subplots
    fig = plt.figure(figsize=(20, 16))
    gs = GridSpec(4, 2, figure=fig, height_ratios=[2, 1, 1, 1])
    
    # Main comparison plot
    ax1 = fig.add_subplot(gs[0, :])
    atomic_numbers = df['Z'].values
    
    # Color by period
    colors = []
    for z in atomic_numbers:
        if z <= 2: colors.append('red')
        elif z <= 10: colors.append('orange')
        elif z <= 18: colors.append('yellow')
        elif z <= 36: colors.append('green')
        elif z <= 54: colors.append('blue')
        elif z <= 86: colors.append('purple')
        else: colors.append('black')
    
    ax1.scatter(atomic_numbers, df['F_coulomb (N)'].values, 
                label='Coulomb Force', color=colors, s=60, alpha=0.7, edgecolors='black')
    ax1.scatter(atomic_numbers, df['F_spin (N)'].values, 
                label='Spin-Tether Force', color=colors, s=60, alpha=0.7, marker='^', edgecolors='black')
    
    ax1.set_yscale('log')
    ax1.set_xlabel('Atomic Number (Z)', fontsize=14)
    ax1.set_ylabel('Force (N)', fontsize=14)
    ax1.set_title('Coulomb vs Spin-Tether Forces Across the Extended Periodic Table\n' + 
                  'Including Relativistic Effects for Heavy Elements', fontsize=16)
    ax1.legend(fontsize=12)
    ax1.grid(True, alpha=0.3)
    
    # Add element labels for notable elements
    for _, row in df.iterrows():
        if row['Symbol'] in ['H', 'He', 'C', 'O', 'Si', 'Fe', 'Cu', 'Ag', 'Au', 'U', 'Pu']:
            ax1.annotate(row['Symbol'], 
                        (row['Z'], row['F_coulomb (N)']), 
                        xytext=(5, 5), textcoords='offset points', fontsize=10, fontweight='bold')
    
    # Agreement percentage subplot
    ax2 = fig.add_subplot(gs[1, 0])
    bars = ax2.bar(atomic_numbers, df['Agreement (%)'].values, color='green', alpha=0.7)
    
    # Highlight elements with <99% agreement
    low_agreement = df[df['Agreement (%)'] < 99]
    if len(low_agreement) > 0:
        ax2.bar(low_agreement['Z'].values, low_agreement['Agreement (%)'].values, 
                color='red', alpha=0.7)
    
    ax2.set_xlabel('Atomic Number (Z)', fontsize=12)
    ax2.set_ylabel('Agreement (%)', fontsize=12)
    ax2.set_title('Force Agreement Percentage', fontsize=14)
    ax2.set_ylim(90, 101)
    ax2.grid(True, alpha=0.3)
    
    # Force ratio subplot
    ax3 = fig.add_subplot(gs[1, 1])
    ax3.plot(atomic_numbers, df['Ratio'].values, 'o-', color='purple', alpha=0.7, markersize=3)
    ax3.axhline(y=1.0, color='black', linestyle='--', alpha=0.5)
    ax3.set_xlabel('Atomic Number (Z)', fontsize=12)
    ax3.set_ylabel('F_spin / F_coulomb', fontsize=12)
    ax3.set_title('Force Ratio', fontsize=14)
    ax3.set_ylim(0.90, 1.10)
    ax3.grid(True, alpha=0.3)
    
    # Relativistic effects subplot
    ax4 = fig.add_subplot(gs[2, :])
    gamma_values = df['Gamma'].values
    ax4.plot(atomic_numbers, gamma_values, 'b-', linewidth=2)
    ax4.set_xlabel('Atomic Number (Z)', fontsize=12)
    ax4.set_ylabel('Relativistic Factor γ', fontsize=12)
    ax4.set_title('Relativistic Corrections Across the Periodic Table', fontsize=14)
    ax4.grid(True, alpha=0.3)
    
    # Add annotations for significant relativistic effects
    for _, row in df.iterrows():
        if row['Gamma'] > 1.1 and row['Symbol'] in ['Au', 'Hg', 'Pb', 'U']:
            ax4.annotate(f"{row['Symbol']}\nγ={row['Gamma']:.3f}", 
                        (row['Z'], row['Gamma']), 
                        xytext=(5, 5), textcoords='offset points', fontsize=9)
    
    # Agreement distribution histogram
    ax5 = fig.add_subplot(gs[3, 0])
    ax5.hist(df['Agreement (%)'].values, bins=20, color='blue', alpha=0.7, edgecolor='black')
    ax5.set_xlabel('Agreement (%)', fontsize=12)
    ax5.set_ylabel('Number of Elements', fontsize=12)
    ax5.set_title('Distribution of Agreement Percentages', fontsize=14)
    ax5.axvline(x=99, color='red', linestyle='--', label='99% threshold')
    ax5.legend()
    
    # Special elements showcase
    ax6 = fig.add_subplot(gs[3, 1])
    special_elements = df[df['Symbol'].isin(['H', 'C', 'Fe', 'Au', 'U'])]
    x_pos = np.arange(len(special_elements))
    
    ax6.bar(x_pos - 0.2, special_elements['F_spin (N)'].values, 0.4, 
            label='F_spin', alpha=0.8)
    ax6.bar(x_pos + 0.2, special_elements['F_coulomb (N)'].values, 0.4, 
            label='F_coulomb', alpha=0.8)
    
    ax6.set_yscale('log')
    ax6.set_xticks(x_pos)
    ax6.set_xticklabels(special_elements['Symbol'].values)
    ax6.set_ylabel('Force (N)', fontsize=12)
    ax6.set_title('Key Elements Comparison', fontsize=14)
    ax6.legend()
    
    plt.tight_layout()
    plt.savefig('periodic_force_comparison_extended.png', dpi=300, bbox_inches='tight')
    plt.savefig('periodic_force_comparison_extended.pdf', bbox_inches='tight')
    print(f"\nPlots saved to: periodic_force_comparison_extended.png and .pdf")
    
    # Statistical summary
    print(f"\n" + "="*70)
    print("STATISTICAL SUMMARY:")
    print(f"Total elements analyzed: {len(df)}")
    print(f"Mean agreement: {df['Agreement (%)'].mean():.2f}%")
    print(f"Median agreement: {df['Agreement (%)'].median():.2f}%")
    print(f"Std deviation: {df['Agreement (%)'].std():.2f}%")
    print(f"Min agreement: {df['Agreement (%)'].min():.2f}% ({df.loc[df['Agreement (%)'].idxmin(), 'Name']})")
    print(f"Max agreement: {df['Agreement (%)'].max():.2f}% ({df.loc[df['Agreement (%)'].idxmax(), 'Name']})")
    
    # Elements with >99% agreement
    high_agreement = df[df['Agreement (%)'] > 99]
    print(f"\nElements with >99% agreement: {len(high_agreement)}/{len(df)} ({100*len(high_agreement)/len(df):.1f}%)")
    
    # Elements with highest relativistic effects
    print(f"\nElements with strongest relativistic effects (γ > 1.1):")
    relativistic = df[df['Gamma'] > 1.1].sort_values('Gamma', ascending=False)
    for _, row in relativistic.head(10).iterrows():
        print(f"  {row['Symbol']:3} ({row['Name']:15}) γ = {row['Gamma']:.4f}")
    
    # Save LaTeX table for paper (top 20 elements)
    latex_elements = df[df['Symbol'].isin(['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne',
                                           'Na', 'Al', 'Si', 'Cl', 'Fe', 'Cu', 'Ag', 'Au', 'Hg', 'U'])]
    
    latex_table = latex_elements[['Symbol', 'Name', 'Z', 'F_spin (N)', 'F_coulomb (N)', 'Agreement (%)']].to_latex(
        index=False,
        float_format=lambda x: f'{x:.3e}' if x < 0.01 or x > 1000 else f'{x:.2f}',
        caption="Comparison of Spin-Tether and Coulomb Forces for Selected Elements",
        label="tab:periodic_comparison_extended"
    )
    
    with open('periodic_force_table_extended.tex', 'w') as f:
        f.write(latex_table)
    print(f"\nLaTeX table saved to: periodic_force_table_extended.tex")
    
    # Create a special plot for transition metals
    fig2, ax = plt.subplots(figsize=(12, 8))
    transition_metals = df[(df['Z'] >= 21) & (df['Z'] <= 30)]  # Sc to Zn
    
    ax.plot(transition_metals['Z'].values, transition_metals['Agreement (%)'].values, 
            'o-', linewidth=2, markersize=8, label='3d Transition Metals')
    
    # Add 4d transition metals
    transition_4d = df[(df['Z'] >= 39) & (df['Z'] <= 48)]  # Y to Cd
    ax.plot(transition_4d['Z'].values, transition_4d['Agreement (%)'].values, 
            's-', linewidth=2, markersize=8, label='4d Transition Metals')
    
    # Add 5d transition metals
    transition_5d = df[(df['Z'] >= 72) & (df['Z'] <= 80)]  # Hf to Hg
    ax.plot(transition_5d['Z'].values, transition_5d['Agreement (%)'].values, 
            '^-', linewidth=2, markersize=8, label='5d Transition Metals')
    
    ax.set_xlabel('Atomic Number (Z)', fontsize=14)
    ax.set_ylabel('Agreement (%)', fontsize=14)
    ax.set_title('Agreement for Transition Metal Series\nShowing d-orbital Effects', fontsize=16)
    ax.legend(fontsize=12)
    ax.grid(True, alpha=0.3)
    ax.set_ylim(95, 101)
    
    # Annotate specific elements
    for series, marker in [(transition_metals, 'o'), (transition_4d, 's'), (transition_5d, '^')]:
        for _, row in series.iterrows():
            if row['Symbol'] in ['Fe', 'Ru', 'Os', 'Au']:
                ax.annotate(row['Symbol'], 
                           (row['Z'], row['Agreement (%)']), 
                           xytext=(5, 5), textcoords='offset points', fontsize=10)
    
    plt.tight_layout()
    plt.savefig('transition_metals_comparison.png', dpi=300, bbox_inches='tight')
    print(f"\nTransition metals plot saved to: transition_metals_comparison.png")
    
    # Create a special plot for lanthanides and actinides
    fig3, (ax_lan, ax_act) = plt.subplots(2, 1, figsize=(14, 10))
    
    # Lanthanides (Ce to Lu, Z=58-71)
    lanthanides = df[(df['Z'] >= 58) & (df['Z'] <= 71)]
    ax_lan.plot(lanthanides['Z'].values, lanthanides['Agreement (%)'].values, 
                'o-', linewidth=2, markersize=8, color='purple', label='Lanthanides (4f)')
    ax_lan.set_ylabel('Agreement (%)', fontsize=12)
    ax_lan.set_title('Agreement for Lanthanides (4f elements)\nShowing f-orbital Effects', fontsize=14)
    ax_lan.grid(True, alpha=0.3)
    ax_lan.set_ylim(95, 101)
    
    # Annotate each lanthanide
    for _, row in lanthanides.iterrows():
        ax_lan.annotate(row['Symbol'], 
                       (row['Z'], row['Agreement (%)']), 
                       xytext=(0, 5), textcoords='offset points', 
                       fontsize=9, ha='center')
    
    # Actinides (Th to Fm, Z=90-100)
    actinides = df[(df['Z'] >= 90) & (df['Z'] <= 100)]
    ax_act.plot(actinides['Z'].values, actinides['Agreement (%)'].values, 
                's-', linewidth=2, markersize=8, color='darkred', label='Actinides (5f)')
    ax_act.set_xlabel('Atomic Number (Z)', fontsize=12)
    ax_act.set_ylabel('Agreement (%)', fontsize=12)
    ax_act.set_title('Agreement for Actinides (5f elements)', fontsize=14)
    ax_act.grid(True, alpha=0.3)
    ax_act.set_ylim(95, 101)
    
    # Annotate each actinide
    for _, row in actinides.iterrows():
        ax_act.annotate(row['Symbol'], 
                       (row['Z'], row['Agreement (%)']), 
                       xytext=(0, 5), textcoords='offset points', 
                       fontsize=9, ha='center')
    
    plt.tight_layout()
    plt.savefig('f_elements_comparison.png', dpi=300, bbox_inches='tight')
    print(f"\nf-elements plot saved to: f_elements_comparison.png")
    
    # Print specific results for Gold to verify relativistic effects
    print(f"\n" + "="*70)
    print("DETAILED GOLD (Au) ANALYSIS:")
    gold = df[df['Symbol'] == 'Au'].iloc[0]
    print(f"Atomic number: Z = {gold['Z']}")
    print(f"Effective nuclear charge: Z_eff = {gold['Z_eff']:.2f}")
    print(f"Quantum numbers: n = {gold['n']}, l = {gold['l']}")
    print(f"Orbital radius: r = {gold['Radius (m)']:.3e} m")
    print(f"Relativistic factor: γ = {gold['Gamma']:.4f}")
    print(f"Spin-tether force: F_spin = {gold['F_spin (N)']:.3e} N")
    print(f"Coulomb force: F_coulomb = {gold['F_coulomb (N)']:.3e} N")
    print(f"Agreement: {gold['Agreement (%)']:.2f}%")
    print(f"This {gold['Agreement (%)']:.1f}% agreement for such a heavy, relativistic atom")
    print("strongly supports the 3D ball model!")


    print("-----------Authors note:-----------")
    print("This script shows that the formula from version 23 was wrong.")
    print("The universe is even simpler, when s=1 we have 100% agreement.")
    print(" across the periodic table")
    print("Or maybe we are still hallucinating.")

    plt.show()


if __name__ == "__main__":
    main()
