{"spec_id":"energy-level-atomic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nenergy-level-atomic: Atomic Energy Level Diagram\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-30\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    annotate,\n    arrow,\n    coord_cartesian,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_point,\n    geom_rect,\n    geom_segment,\n    geom_text,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_fill_identity,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme tokens (Imprint palette — see prompts/default-style-guide.md)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — semantic spectral mapping (UV=lavender, visible=matte red)\nLYMAN_COLOR = \"#C475FD\"  # Imprint position 2 — lavender → UV spectral region\nBALMER_COLOR = \"#AE3030\"  # Imprint position 5 — matte red → visible light anchor\nIONIZATION_COLOR = \"#009E73\"  # Imprint position 1 — brand green for ionization limit\nSERIES_COLORS = {\"Lyman (UV)\": LYMAN_COLOR, \"Balmer (Visible)\": BALMER_COLOR}\n# Dark theme: Balmer labels over dark brownish-red band have ~2.7:1 contrast; use INK_SOFT instead\nBALMER_LABEL_COLOR = BALMER_COLOR if THEME == \"light\" else INK_SOFT\n\n# Data — Hydrogen atom energy levels (En = -13.6/n² eV)\nn_values = np.arange(1, 7)\nenergies = -13.6 / n_values**2\n\n# Nonlinear visual transform: sign(E)*sqrt(|E|) spreads crowded upper levels\n# and compresses the large n=1→n=2 gap (spec: \"consider nonlinear scale\")\nvis_e = np.sign(energies) * np.sqrt(np.abs(energies))\n\n# Energy level lines (partial-width per spec)\nlevels = pd.DataFrame({\"y\": vis_e, \"x_start\": 0.15, \"x_end\": 0.78})\n\n# Endpoint dots at each end of level lines\nendpoints = pd.DataFrame({\"x\": [0.15] * 6 + [0.78] * 6, \"y\": list(vis_e) * 2})\n\n# Right-side labels — slight vertical nudge for n=5,6 to prevent overlap\nlabel_y = list(vis_e[:4]) + [vis_e[4] + 0.08, vis_e[5] + 0.20]\nlabels_df = pd.DataFrame(\n    {\"y\": label_y, \"label\": [f\"n = {n}  ({e:.2f} eV)\" for n, e in zip(n_values, energies, strict=True)], \"x\": 0.91}\n)\n\n# Thin connector lines from level endpoints to spread labels\nconnectors = pd.DataFrame({\"x1\": [0.79] * 6, \"x2\": [0.89] * 6, \"y1\": list(vis_e), \"y2\": label_y})\n\n# Ionization limit at 0 eV\nion_y = 0.0\nion_label_y = 0.16\n\n# Transitions — grouped by Lyman (UV) and Balmer (Visible) series\nenergy_lookup = dict(zip(n_values, vis_e, strict=True))\ntransitions = pd.DataFrame(\n    {\n        \"from_n\": [2, 3, 4, 3, 4, 5],\n        \"to_n\": [1, 1, 1, 2, 2, 2],\n        \"x_pos\": [0.28, 0.36, 0.44, 0.54, 0.62, 0.70],\n        \"series\": [\"Lyman (UV)\"] * 3 + [\"Balmer (Visible)\"] * 3,\n        \"label\": [\"Ly-α\\n122 nm\", \"Ly-β\\n103 nm\", \"Ly-γ\\n97 nm\", \"Hα\\n656 nm\", \"Hβ\\n486 nm\", \"Hγ\\n434 nm\"],\n    }\n)\ntransitions[\"y_start\"] = transitions[\"from_n\"].map(energy_lookup) - 0.04\ntransitions[\"y_end\"] = transitions[\"to_n\"].map(energy_lookup) + 0.08\ntransitions[\"label_y\"] = (transitions[\"y_start\"] + transitions[\"y_end\"]) / 2\n\n# Series group labels above the diagram (serve as inline legend)\nseries_labels = pd.DataFrame(\n    {\n        \"x\": [0.36, 0.62],\n        \"y\": [0.30, 0.30],\n        \"label\": [\"Lyman Series (UV)\", \"Balmer Series (Visible)\"],\n        \"series\": [\"Lyman (UV)\", \"Balmer (Visible)\"],\n    }\n)\n\n# Shaded bands behind each series (theme-adaptive fill)\nband_fill_lyman = \"#EDE9FE\" if THEME == \"light\" else \"#2A1A40\"\nband_fill_balmer = \"#FEE2E2\" if THEME == \"light\" else \"#3C1A1A\"\nseries_bands = pd.DataFrame(\n    {\n        \"xmin\": [0.23, 0.49],\n        \"xmax\": [0.49, 0.75],\n        \"ymin\": [vis_e[0] - 0.12, vis_e[1] - 0.12],\n        \"ymax\": [vis_e[3] + 0.08, vis_e[4] + 0.08],\n        \"fill\": [band_fill_lyman, band_fill_balmer],\n    }\n)\n\n# Y-axis: real eV values at transformed tick positions\ny_breaks = [vis_e[0], vis_e[1], vis_e[2], vis_e[3], 0.0]\ny_labels = [\"-13.6\", \"-3.4\", \"-1.51\", \"-0.85\", \"0\"]\n\n# Title (51 chars — under 67 baseline, default 12pt applies)\ntitle = \"energy-level-atomic · python · plotnine · anyplot.ai\"\n\n# Build plot — layered grammar: bands → levels → ionization → connectors → labels → arrows\nplot = (\n    ggplot()\n    # Background shaded bands for series grouping\n    + geom_rect(\n        data=series_bands, mapping=aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\", fill=\"fill\"), alpha=0.40\n    )\n    + scale_fill_identity()\n    # Energy level lines (structural — INK_SOFT, partial width per spec)\n    + geom_segment(data=levels, mapping=aes(x=\"x_start\", xend=\"x_end\", y=\"y\", yend=\"y\"), color=INK_SOFT, size=1.8)\n    # Endpoint dots for level lines\n    + geom_point(data=endpoints, mapping=aes(x=\"x\", y=\"y\"), color=INK_SOFT, size=2.5)\n    # Ionization limit (dashed reference line + label) — brand green structural marker\n    + annotate(\"segment\", x=0.15, xend=0.78, y=ion_y, yend=ion_y, color=IONIZATION_COLOR, size=1.2, linetype=\"dashed\")\n    + annotate(\n        \"text\", x=0.91, y=ion_label_y, label=\"Ionization\\nlimit (0 eV)\", size=3, ha=\"left\", color=IONIZATION_COLOR\n    )\n    # Connector lines from level endpoints to spread labels\n    + geom_segment(data=connectors, mapping=aes(x=\"x1\", xend=\"x2\", y=\"y1\", yend=\"y2\"), color=INK_MUTED, size=0.4)\n    # Ionization connector\n    + annotate(\"segment\", x=0.79, xend=0.89, y=ion_y, yend=ion_label_y, color=IONIZATION_COLOR, size=0.4)\n    # Level labels (right side, primary ink)\n    + geom_text(data=labels_df, mapping=aes(x=\"x\", y=\"y\", label=\"label\"), size=3, ha=\"left\", color=INK)\n    # Transition arrows (emission = downward, colored by series via scale_color_manual)\n    + geom_segment(\n        data=transitions,\n        mapping=aes(x=\"x_pos\", xend=\"x_pos\", y=\"y_start\", yend=\"y_end\", color=\"series\"),\n        size=1.2,\n        arrow=arrow(length=0.08, type=\"closed\"),\n    )\n    # Transition wavelength labels — split by series for theme-adaptive Balmer contrast\n    + geom_text(\n        data=transitions[transitions[\"series\"] == \"Lyman (UV)\"],\n        mapping=aes(x=\"x_pos\", y=\"label_y\", label=\"label\"),\n        size=2.8,\n        nudge_x=-0.045,\n        color=LYMAN_COLOR,\n    )\n    + geom_text(\n        data=transitions[transitions[\"series\"] == \"Balmer (Visible)\"],\n        mapping=aes(x=\"x_pos\", y=\"label_y\", label=\"label\"),\n        size=2.8,\n        nudge_x=-0.045,\n        color=BALMER_LABEL_COLOR,\n    )\n    # Series group labels at the top (inline legend, series-matched color)\n    + geom_text(data=series_labels, mapping=aes(x=\"x\", y=\"y\", label=\"label\", color=\"series\"), size=3.5)\n    # Named color scale — idiomatic plotnine: data-driven aesthetic mapping\n    + scale_color_manual(values=SERIES_COLORS)\n    + scale_x_continuous(limits=(0.0, 1.18), expand=(0, 0))\n    + scale_y_continuous(name=\"Energy (eV)\", breaks=y_breaks, labels=y_labels)\n    + coord_cartesian(ylim=(vis_e[0] - 0.25, 0.52))\n    + labs(title=title, subtitle=\"Hydrogen Atom Emission Spectrum  —  Lyman (UV) & Balmer (Visible) Series\", x=\"\")\n    + theme_minimal()\n    + theme(\n        figure_size=(8, 4.5),\n        plot_title=element_text(size=12, weight=\"bold\", color=INK),\n        plot_subtitle=element_text(size=7, color=INK_SOFT),\n        axis_title_y=element_text(size=10, color=INK),\n        axis_title_x=element_blank(),\n        axis_text_y=element_text(size=8, color=INK_SOFT),\n        axis_text_x=element_blank(),\n        axis_ticks_major_x=element_blank(),\n        panel_grid_major_x=element_blank(),\n        panel_grid_minor_x=element_blank(),\n        panel_grid_minor_y=element_blank(),\n        panel_grid_major_y=element_line(alpha=0.12, color=INK),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        legend_position=\"none\",\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\")\n"}