{"spec_id":"line-reaction-coordinate","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nline-reaction-coordinate: Reaction Coordinate Energy Diagram\nLibrary: letsplot 4.10.1 | Python 3.13.14\nQuality: 89/100 | Updated: 2026-06-24\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    arrow,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_area,\n    geom_label,\n    geom_line,\n    geom_segment,\n    ggplot,\n    ggsize,\n    labs,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n)\nfrom lets_plot.export import ggsave\nfrom scipy.interpolate import CubicSpline\n\n\nLetsPlot.setup_html()\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\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID_COLOR = \"#D5D4CC\" if THEME == \"light\" else \"#2A2A26\"\n\n# Imprint palette\nCURVE_COLOR = \"#009E73\"  # Imprint position 1 — main energy curve\nEA_COLOR = \"#AE3030\"  # Imprint position 5 (matte red) — activation energy barrier\nDH_COLOR = \"#4467A3\"  # Imprint position 3 (blue) — enthalpy change\n\n# Data: single-step exothermic reaction via cubic spline interpolation\n# Control points chosen for realistic reactant plateau, TS peak, and product plateau\nreactant_energy = 50.0\ntransition_energy = 120.0\nproduct_energy = 20.0\npeak_pos = 0.45\n\nctrl_x = np.array([0.0, 0.10, 0.25, 0.45, 0.60, 0.78, 0.90, 1.0])\nctrl_y = np.array([50.0, 50.0, 72.0, 120.0, 60.0, 22.0, 20.0, 20.0])\ncs = CubicSpline(ctrl_x, ctrl_y, bc_type=((1, 0.0), (1, 0.0)))\n\nreaction_coord = np.linspace(0, 1, 400)\nenergy = cs(reaction_coord)\ndf = pd.DataFrame({\"reaction_coordinate\": reaction_coord, \"energy\": energy})\n\nea = transition_energy - reactant_energy\ndelta_h = product_energy - reactant_energy\n\n# Horizontal reference dashed lines at reactant and product energy levels\nhline_df = pd.DataFrame(\n    {\n        \"x\": [0.0, 0.0],\n        \"xend\": [1.0, 1.0],\n        \"y\": [reactant_energy, product_energy],\n        \"yend\": [reactant_energy, product_energy],\n    }\n)\n\n# Ea double-headed arrow\nea_x = 0.22\nea_arrow_df = pd.DataFrame(\n    {\n        \"x\": [ea_x, ea_x],\n        \"y\": [reactant_energy + 2, transition_energy - 2],\n        \"xend\": [ea_x, ea_x],\n        \"yend\": [transition_energy - 2, reactant_energy + 2],\n    }\n)\n\n# ΔH double-headed arrow\ndh_x = 0.80\ndh_arrow_df = pd.DataFrame(\n    {\n        \"x\": [dh_x, dh_x],\n        \"y\": [reactant_energy - 2, product_energy + 2],\n        \"xend\": [dh_x, dh_x],\n        \"yend\": [product_energy + 2, reactant_energy - 2],\n    }\n)\n\n# Title font scaling (57 chars < 67 baseline → no scaling needed)\ntitle_str = \"line-reaction-coordinate · python · letsplot · anyplot.ai\"\nn = len(title_str)\ndefault_fontsize = 16\nratio = 67 / n if n > 67 else 1.0\ntitle_fontsize = max(11, round(default_fontsize * ratio))\n\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_border=element_blank(),\n    panel_grid_major_y=element_line(color=GRID_COLOR, size=0.4),\n    panel_grid_minor=element_blank(),\n    panel_grid_major_x=element_blank(),\n    axis_title=element_text(color=INK, size=12),\n    axis_text=element_text(color=INK_SOFT, size=10),\n    axis_text_x=element_blank(),\n    axis_ticks_x=element_blank(),\n    axis_line_x=element_line(color=INK_SOFT, size=0.5),\n    axis_line_y=element_line(color=INK_SOFT, size=0.5),\n    plot_title=element_text(color=INK, size=title_fontsize, hjust=0.5),\n    legend_position=\"none\",\n)\n\nplot = (\n    ggplot(df, aes(x=\"reaction_coordinate\", y=\"energy\"))\n    + geom_area(fill=CURVE_COLOR, alpha=0.08)\n    + geom_segment(\n        data=hline_df, mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"), linetype=\"dashed\", color=INK_MUTED, size=0.7\n    )\n    + geom_line(color=CURVE_COLOR, size=2.5)\n    + geom_segment(\n        data=ea_arrow_df,\n        mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"),\n        color=EA_COLOR,\n        size=1.3,\n        arrow=arrow(length=10, type=\"open\"),\n    )\n    + geom_segment(\n        data=dh_arrow_df,\n        mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"),\n        color=DH_COLOR,\n        size=1.3,\n        arrow=arrow(length=10, type=\"open\"),\n    )\n    + geom_label(\n        data=pd.DataFrame({\"x\": [0.08], \"y\": [reactant_energy - 9], \"label\": [\"Reactants\\n50 kJ/mol\"]}),\n        mapping=aes(x=\"x\", y=\"y\", label=\"label\"),\n        size=4.5,\n        color=INK,\n        fill=ELEVATED_BG,\n        alpha=0.9,\n        label_padding=0.4,\n        label_r=0.3,\n        label_size=0,\n    )\n    + geom_label(\n        data=pd.DataFrame({\"x\": [peak_pos], \"y\": [transition_energy + 9], \"label\": [\"Transition State\\n120 kJ/mol\"]}),\n        mapping=aes(x=\"x\", y=\"y\", label=\"label\"),\n        size=4.5,\n        color=INK,\n        fill=ELEVATED_BG,\n        alpha=0.9,\n        label_padding=0.4,\n        label_r=0.3,\n        label_size=0,\n    )\n    + geom_label(\n        data=pd.DataFrame({\"x\": [0.92], \"y\": [product_energy - 9], \"label\": [\"Products\\n20 kJ/mol\"]}),\n        mapping=aes(x=\"x\", y=\"y\", label=\"label\"),\n        size=4.5,\n        color=INK,\n        fill=ELEVATED_BG,\n        alpha=0.9,\n        label_padding=0.4,\n        label_r=0.3,\n        label_size=0,\n    )\n    + geom_label(\n        data=pd.DataFrame(\n            {\"x\": [ea_x], \"y\": [(reactant_energy + transition_energy) / 2], \"label\": [f\"Ea = {ea:.0f} kJ/mol\"]}\n        ),\n        mapping=aes(x=\"x\", y=\"y\", label=\"label\"),\n        size=4.0,\n        color=\"#F0EFE8\",\n        fill=EA_COLOR,\n        alpha=0.9,\n        label_padding=0.5,\n        label_r=0.3,\n        label_size=0,\n        fontface=\"bold\",\n    )\n    + geom_label(\n        data=pd.DataFrame(\n            {\"x\": [dh_x], \"y\": [(reactant_energy + product_energy) / 2], \"label\": [f\"ΔH = {delta_h:.0f} kJ/mol\"]}\n        ),\n        mapping=aes(x=\"x\", y=\"y\", label=\"label\"),\n        size=4.0,\n        color=\"#F0EFE8\",\n        fill=DH_COLOR,\n        alpha=0.9,\n        label_padding=0.5,\n        label_r=0.3,\n        label_size=0,\n        fontface=\"bold\",\n    )\n    + scale_x_continuous(name=\"Reaction Coordinate\", breaks=[], expand=[0.02, 0.02])\n    + scale_y_continuous(name=\"Potential Energy (kJ/mol)\", limits=[0, 145])\n    + labs(title=title_str)\n    + ggsize(800, 450)\n    + anyplot_theme\n)\n\n# Save\nggsave(plot, filename=f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, filename=f\"plot-{THEME}.html\", path=\".\")\n"}