{"spec_id":"spectrum-nmr","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nspectrum-nmr: NMR Spectrum (Nuclear Magnetic Resonance)\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-06-03\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pygal\nfrom pygal.style import Style\n\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — hybrid-v3 sort; position 1 (#009E73) always first series\nIMPRINT_PALETTE = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# Data: Synthetic 1H NMR spectrum of ethanol (CH3-CH2-OH)\nnp.random.seed(42)\nchemical_shift = np.linspace(0, 12, 6000)\nw = 0.010  # Lorentzian half-width for multiplet peaks\n\n# Build spectrum with Lorentzian line shapes: h·w²/((x−c)²+w²)\nintensity = np.zeros_like(chemical_shift)\n\n# TMS reference peak at 0 ppm (singlet)\nintensity += 0.30 * 0.008**2 / ((chemical_shift - 0.00) ** 2 + 0.008**2)\n\n# CH3 triplet near 1.18 ppm (1:2:1 intensity pattern, J = 7 Hz)\ntc, j = 1.18, 0.07\nintensity += 0.50 * w**2 / ((chemical_shift - (tc - j)) ** 2 + w**2)\nintensity += 1.00 * w**2 / ((chemical_shift - tc) ** 2 + w**2)\nintensity += 0.50 * w**2 / ((chemical_shift - (tc + j)) ** 2 + w**2)\n\n# CH2 quartet near 3.69 ppm (1:3:3:1 intensity pattern, J = 7 Hz)\nqc = 3.69\nintensity += 0.25 * w**2 / ((chemical_shift - (qc - 1.5 * j)) ** 2 + w**2)\nintensity += 0.75 * w**2 / ((chemical_shift - (qc - 0.5 * j)) ** 2 + w**2)\nintensity += 0.75 * w**2 / ((chemical_shift - (qc + 0.5 * j)) ** 2 + w**2)\nintensity += 0.25 * w**2 / ((chemical_shift - (qc + 1.5 * j)) ** 2 + w**2)\n\n# OH singlet near 2.61 ppm\nintensity += 0.35 * 0.015**2 / ((chemical_shift - 2.61) ** 2 + 0.015**2)\n\n# Subtle baseline noise\nintensity += np.random.normal(0, 0.003, len(chemical_shift))\nintensity = np.clip(intensity, 0, None)\n\n# Downsample for pygal performance (every 6th point → ~1000 points)\ncs_plot = chemical_shift[::6]\nint_plot = intensity[::6]\n\n# Negate x-values to reverse axis (NMR convention: high ppm on left)\ncs_negated = -cs_plot\n\n# Peak annotations with functional group labels\npeak_info = [(0.00, \"TMS\"), (1.18, \"CH₃ triplet\"), (2.61, \"OH singlet\"), (3.69, \"CH₂ quartet\")]\n\n# Style — Imprint palette, theme-adaptive chrome, canonical 3200×1800 sizing\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT_PALETTE,\n    title_font_size=66,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=44,\n    value_font_size=40,\n    stroke_width=2.5,\n)\n\n# Chart — 3200×1800 landscape (canonical canvas for native-pixel pygal)\ntitle = \"Ethanol ¹H NMR · spectrum-nmr · python · pygal · anyplot.ai\"\nchart = pygal.XY(\n    style=custom_style,\n    width=3200,\n    height=1800,\n    title=title,\n    x_title=\"Chemical Shift (ppm)\",\n    y_title=\"Intensity (a.u.)\",\n    show_dots=False,\n    print_labels=True,\n    print_values=False,\n    show_x_guides=False,\n    show_y_guides=True,\n    show_legend=False,\n    xrange=(-12.5, 0.8),\n    range=(-0.02, 1.12),\n    margin=35,\n    margin_top=60,\n    margin_bottom=80,\n    margin_left=75,\n    margin_right=300,\n    tooltip_fancy_mode=True,\n    tooltip_border_radius=8,\n    x_value_formatter=lambda x: f\"{abs(x):.1f}\",\n    y_value_formatter=lambda y: f\"{y:.3f}\",\n    x_labels_major_every=2,\n    css=[\n        \"file://style.css\",\n        \"file://graph.css\",\n        \"inline:.axis > .line { stroke: transparent !important; }\",\n        \"inline:.chart-background { rx: 0; ry: 0; }\",\n        f\"inline:.text-overlay .series .label {{ font-size: 38px !important; fill: {INK} !important; font-weight: bold !important; }}\",\n    ],\n)\n\n# Spectrum line — stroke_style width=4 for crisp visibility at 3200×1800\nspectrum_points = [(float(cs), float(inten)) for cs, inten in zip(cs_negated, int_plot, strict=False)]\nchart.add(\"¹H NMR Spectrum\", spectrum_points, stroke_style={\"width\": 4}, fill=False)\n\n# Peak markers — search in downsampled arrays so dots snap to spectrum line\nfor ppm, group_name in peak_info:\n    mask = np.abs(cs_plot - ppm) < 0.15\n    region_idx = np.where(mask)[0]\n    idx = int(region_idx[np.argmax(int_plot[region_idx])])\n    peak_x = -float(cs_plot[idx])\n    peak_y = float(int_plot[idx])\n    label_text = f\"{group_name} ({ppm:.2f} ppm)\"\n    legend_label = f\"{group_name} ({ppm:.1f})\"\n    point = {\"value\": (peak_x, peak_y), \"label\": label_text}\n    chart.add(legend_label, [point], stroke=False, show_dots=True, dots_size=14)\n\n# Save PNG and interactive HTML\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}