{"spec_id":"energy-level-atomic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nenergy-level-atomic: Atomic Energy Level Diagram\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-30\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Imprint palette — canonical order, first series always #009E73\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Seaborn theme — theme-adaptive chrome\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data — hydrogen atom energy levels E_n = -13.6 / n² eV\nenergy_values = {\"n=1\": -13.60, \"n=2\": -3.40, \"n=3\": -1.51, \"n=4\": -0.85, \"n=5\": -0.54, \"n=6\": -0.38}\n\n# Nonlinear visual y-positions so upper converging levels remain readable\nvisual_y = {\"n=1\": 0.0, \"n=2\": 3.5, \"n=3\": 5.5, \"n=4\": 7.0, \"n=5\": 8.2, \"n=6\": 9.2}\nionization_y = 10.5\n\n# Spectral series transitions (upper → lower = emission)\ntransition_data = [\n    (\"n=2\", \"n=1\", \"Lyman\", 122),\n    (\"n=3\", \"n=1\", \"Lyman\", 103),\n    (\"n=4\", \"n=1\", \"Lyman\", 97),\n    (\"n=3\", \"n=2\", \"Balmer\", 656),\n    (\"n=4\", \"n=2\", \"Balmer\", 486),\n    (\"n=5\", \"n=2\", \"Balmer\", 434),\n    (\"n=4\", \"n=3\", \"Paschen\", 1875),\n    (\"n=5\", \"n=3\", \"Paschen\", 1282),\n    (\"n=6\", \"n=3\", \"Paschen\", 1094),\n]\n\ntransition_df = pd.DataFrame(transition_data, columns=[\"upper\", \"lower\", \"series\", \"wavelength_nm\"])\ntransition_df[\"y_top\"] = transition_df[\"upper\"].map(visual_y)\ntransition_df[\"y_bot\"] = transition_df[\"lower\"].map(visual_y)\n\n# Series colors from Imprint palette — CVD-safe canonical order\nseries_names = [\"Lyman\", \"Balmer\", \"Paschen\"]\nseries_colors = {\n    \"Lyman\": IMPRINT_PALETTE[0],  # #009E73 — Imprint position 1\n    \"Balmer\": IMPRINT_PALETTE[1],  # #C475FD — Imprint position 2\n    \"Paschen\": IMPRINT_PALETTE[2],  # #4467A3 — Imprint position 3\n}\n\n# Subtle background column tints — seaborn palette functions (light/dark adaptive)\nseries_bg = {}\nfor name, color in series_colors.items():\n    if THEME == \"light\":\n        series_bg[name] = sns.light_palette(color, n_colors=8)[1]\n    else:\n        series_bg[name] = sns.dark_palette(color, n_colors=8, reverse=True)[1]\n\n# X column positions; Paschen shifted right to give Balmer/Paschen headers breathing room\nseries_x_base = {\"Lyman\": 0.18, \"Balmer\": 0.42, \"Paschen\": 0.70}\ncol_spacing = {\"Lyman\": 0.048, \"Balmer\": 0.048, \"Paschen\": 0.060}\n\nfor series in series_names:\n    mask = transition_df[\"series\"] == series\n    sp = col_spacing[series]\n    transition_df.loc[mask, \"x_pos\"] = [series_x_base[series] + i * sp for i in range(mask.sum())]\n\n# Figure — canvas: 3200 × 1800 px (16:9 landscape)\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n# Reserve right margin for energy value labels (clip_on=False text extends here)\nfig.subplots_adjust(left=0.04, right=0.60, top=0.90, bottom=0.04)\n\nline_xmin, line_xmax = 0.06, 0.84\n\n# Background column tints\nfor series in series_names:\n    x_base = series_x_base[series]\n    sp = col_spacing[series]\n    n_trans = (transition_df[\"series\"] == series).sum()\n    band_left = x_base - 0.028\n    band_right = x_base + (n_trans - 1) * sp + 0.042\n    alpha = 0.12 if THEME == \"light\" else 0.10\n    ax.axvspan(band_left, band_right, alpha=alpha, color=series_bg[series], zorder=0)\n\n# Energy level lines via sns.lineplot (DataFrame-driven)\nlevel_rows = []\nfor label, y_pos in visual_y.items():\n    level_rows += [{\"x\": line_xmin, \"y\": y_pos, \"level\": label}, {\"x\": line_xmax, \"y\": y_pos, \"level\": label}]\nlevel_df = pd.DataFrame(level_rows)\n\nsns.lineplot(\n    data=level_df, x=\"x\", y=\"y\", units=\"level\", estimator=None, color=INK, linewidth=2.0, ax=ax, legend=False, zorder=3\n)\n\n# Level endpoints via sns.scatterplot\nsns.scatterplot(data=level_df, x=\"x\", y=\"y\", color=INK, s=25, zorder=4, ax=ax, legend=False, edgecolor=\"none\")\n\n# Energy value labels — clip_on=False so text extends into right margin\nfor label, y_pos in visual_y.items():\n    energy = energy_values[label]\n    ax.text(\n        line_xmax + 0.018,\n        y_pos,\n        f\"{label}  ({energy:.2f} eV)\",\n        fontsize=9,\n        va=\"center\",\n        ha=\"left\",\n        color=INK,\n        fontweight=\"medium\",\n        clip_on=False,\n    )\n\n# Ionization limit (dashed reference line)\nax.hlines(ionization_y, line_xmin, line_xmax, colors=INK_SOFT, linewidth=1.5, linestyles=\"dashed\", zorder=3)\nax.text(\n    line_xmax + 0.018,\n    ionization_y,\n    \"Ionization  (0.00 eV)\",\n    fontsize=9,\n    va=\"center\",\n    ha=\"left\",\n    color=INK_SOFT,\n    fontweight=\"medium\",\n    clip_on=False,\n)\n\n# Transition arrows (emission: downward)\ngap = 0.18\nfor _, row in transition_df.iterrows():\n    color = series_colors[row[\"series\"]]\n    ax.annotate(\n        \"\",\n        xy=(row[\"x_pos\"], row[\"y_bot\"] + gap),\n        xytext=(row[\"x_pos\"], row[\"y_top\"] - gap),\n        arrowprops={\"arrowstyle\": \"->,head_width=0.28,head_length=0.18\", \"color\": color, \"linewidth\": 1.8},\n        zorder=2,\n    )\n    mid_y = (row[\"y_top\"] + row[\"y_bot\"]) / 2\n    ax.text(\n        row[\"x_pos\"] + 0.012,\n        mid_y,\n        f\"{row['wavelength_nm']} nm\",\n        fontsize=8,\n        color=color,\n        va=\"center\",\n        ha=\"left\",\n        rotation=90,\n        alpha=0.9,\n    )\n\n# Series labels and spectral region subtitles\nspectral_regions = {\"Lyman\": \"ultraviolet\", \"Balmer\": \"visible\", \"Paschen\": \"infrared\"}\nfor series in series_names:\n    x_base = series_x_base[series]\n    sp = col_spacing[series]\n    n_trans = (transition_df[\"series\"] == series).sum()\n    x_center = x_base + sp * (n_trans - 1) / 2\n    color = series_colors[series]\n    ax.text(x_center, ionization_y + 0.82, f\"{series} series\", fontsize=8, fontweight=\"bold\", ha=\"center\", color=color)\n    ax.text(\n        x_center,\n        ionization_y + 0.22,\n        f\"({spectral_regions[series]})\",\n        fontsize=7,\n        ha=\"center\",\n        color=color,\n        alpha=0.75,\n        style=\"italic\",\n    )\n\n# Energy direction arrow (left margin indicator)\nax.annotate(\n    \"\", xy=(0.02, 10.8), xytext=(0.02, -0.3), arrowprops={\"arrowstyle\": \"-|>\", \"color\": INK_SOFT, \"linewidth\": 1.2}\n)\nax.text(0.027, 5.25, \"Energy\", fontsize=8.5, rotation=90, va=\"center\", ha=\"left\", color=INK_SOFT)\n\n# Axes cleanup\nax.set_xlim(-0.01, 1.15)\nax.set_ylim(-0.5, 12.2)\n\ntitle = \"energy-level-atomic · python · seaborn · anyplot.ai\"\nax.set_title(title, fontsize=12, fontweight=\"medium\", pad=10, color=INK)\nax.set_xticks([])\nax.set_yticks([])\nax.set_xlabel(\"\")\nax.set_ylabel(\"\")\nsns.despine(ax=ax, left=True, bottom=True)\n\n# Save — no bbox_inches so canvas stays at exactly 3200 × 1800\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}