{"spec_id":"skewt-logp-atmospheric","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nskewt-logp-atmospheric: Skew-T Log-P Atmospheric Diagram\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-21\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.lines import Line2D\nfrom matplotlib.patches import Patch\nfrom matplotlib.ticker import ScalarFormatter\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n# Boost reference line visibility on dark background\nREF_ALPHA = 0.10 if THEME == \"light\" else 0.18\n\n# Okabe-Ito — data series\nTEMP_COLOR = \"#009E73\"  # temperature — position 1\nDEWPOINT_COLOR = \"#C475FD\"  # dewpoint — position 2\nCAPE_COLOR = \"#AE3030\"  # CAPE shading — position 5\nCIN_COLOR = \"#4467A3\"  # CIN shading — position 3\n\n# Data — simulated mid-latitude radiosonde sounding (surface to upper troposphere)\nnp.random.seed(42)\npressure = np.array([1000, 950, 900, 850, 800, 750, 700, 650, 600, 550, 500, 450, 400, 350, 300, 250, 200, 150, 100])\ntemperature = np.array([25, 22, 19, 15, 12, 8, 5, 1, -3, -8, -14, -21, -28, -37, -45, -52, -56, -58, -56])\ndewpoint = np.array([18, 16, 14, 10, 6, 2, -2, -8, -15, -22, -28, -35, -42, -50, -55, -60, -65, -70, -75])\n\n# Lifted parcel — Bolton (1980) LCL + simplified moist adiabat above for CAPE/CIN shading\nT0_K = temperature[0] + 273.15\nTd0_K = dewpoint[0] + 273.15\nP0 = float(pressure[0])\nT_lcl_K = 1.0 / (1.0 / (Td0_K - 56.0) + np.log(T0_K / Td0_K) / 800.0) + 56.0\nP_lcl = P0 * (T_lcl_K / T0_K) ** 3.5  # ~902 hPa\nparcel_temp = np.where(\n    pressure > P_lcl,\n    T0_K * (pressure / P0) ** 0.286 - 273.15,  # dry adiabatic below LCL\n    T_lcl_K * (pressure / P_lcl) ** 0.19 - 273.15,  # moist adiabatic above LCL\n)\n\n# Plot — square canvas for symmetric atmospheric profile\nfig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\nax.set_yscale(\"log\")\nax.set_ylim(1050, 100)\nax.set_xlim(-80, 50)\n\nax.yaxis.set_major_formatter(ScalarFormatter())\nax.set_yticks([1000, 850, 700, 500, 400, 300, 250, 200, 150, 100])\n\n# Precompute skew offsets (45-degree skew of temperature axis)\np0_ref = 1000.0\np_range = np.logspace(np.log10(1050), np.log10(100), 100)\nskew_ref = 45.0 * (np.log(p_range) - np.log(p0_ref)) / (np.log(100) - np.log(p0_ref))\nskew_data = 45.0 * (np.log(pressure) - np.log(p0_ref)) / (np.log(100) - np.log(p0_ref))\n\n# Reference lines — isotherms (constant temperature, skewed 45°)\nfor t in np.arange(-80, 60, 10):\n    ax.plot(t + skew_ref, p_range, color=\"#8B4513\", alpha=REF_ALPHA, linewidth=0.5)\n\n# Dry adiabats (constant potential temperature)\nfor theta in np.arange(-30, 150, 10):\n    t_dry = (theta + 273.15) * (p_range / 1000) ** 0.286 - 273.15\n    mask = (t_dry > -80) & (t_dry < 50)\n    if np.any(mask):\n        ax.plot(\n            t_dry[mask] + skew_ref[mask],\n            p_range[mask],\n            color=\"#228B22\",\n            alpha=REF_ALPHA * 1.3,\n            linewidth=0.5,\n            linestyle=\"--\",\n        )\n\n# Moist adiabats (simplified saturated-adiabatic lapse rate)\nfor theta_e in np.arange(0, 50, 4):\n    t_dry = (theta_e + 273.15) * (p_range / 1000) ** 0.286 - 273.15\n    moisture_factor = np.clip((t_dry + 30) / 60, 0, 1) * 0.5\n    t_moist = t_dry * (1 - moisture_factor * (1 - (p_range / 1000) ** 0.2))\n    mask = (t_moist > -80) & (t_moist < 50)\n    if np.any(mask):\n        ax.plot(\n            t_moist[mask] + skew_ref[mask],\n            p_range[mask],\n            color=\"#4169E1\",\n            alpha=REF_ALPHA * 1.1,\n            linewidth=0.5,\n            linestyle=\"-.\",\n        )\n\n# Mixing ratio lines (constant water vapor mixing ratio)\nfor w in [0.5, 1, 2, 4, 7, 10, 15, 20]:\n    e = (w * p_range) / (622 + w)\n    td = 243.5 * np.log(e / 6.112) / (17.67 - np.log(e / 6.112))\n    mask = (td > -80) & (td < 50) & (p_range >= 400)\n    if np.any(mask):\n        ax.plot(\n            td[mask] + skew_ref[mask],\n            p_range[mask],\n            color=\"#9932CC\",\n            alpha=REF_ALPHA * 1.3,\n            linewidth=0.5,\n            linestyle=\":\",\n        )\n\n# Skewed coordinates for profiles\ntemp_skewed = temperature + skew_data\ndewpoint_skewed = dewpoint + skew_data\nparcel_skewed = parcel_temp + skew_data\n\n# CAPE/CIN shading — meteorological insight into convective potential\nax.fill_betweenx(\n    pressure,\n    temp_skewed,\n    parcel_skewed,\n    where=(parcel_temp < temperature),\n    alpha=0.18,\n    color=CIN_COLOR,\n    zorder=2,\n    interpolate=True,\n)\nax.fill_betweenx(\n    pressure,\n    temp_skewed,\n    parcel_skewed,\n    where=(parcel_temp >= temperature),\n    alpha=0.25,\n    color=CAPE_COLOR,\n    zorder=2,\n    interpolate=True,\n)\n\n# Lifted parcel trace\nax.plot(parcel_skewed, pressure, color=INK_MUTED, linewidth=1.0, linestyle=\":\", zorder=3.5, alpha=0.65)\n\n# 500 hPa emphasis — key synoptic reference level\nax.axhline(y=500, color=INK_SOFT, linewidth=1.5, alpha=0.4, linestyle=\"-\", zorder=1.5)\n\n# Data profiles\nax.plot(temp_skewed, pressure, color=TEMP_COLOR, linewidth=2.5, solid_capstyle=\"round\", zorder=4)\nax.scatter(temp_skewed, pressure, color=TEMP_COLOR, s=120, zorder=5, edgecolors=PAGE_BG, linewidth=0.8)\n\nax.plot(dewpoint_skewed, pressure, color=DEWPOINT_COLOR, linewidth=2.5, linestyle=\"--\", dash_capstyle=\"round\", zorder=4)\nax.scatter(dewpoint_skewed, pressure, color=DEWPOINT_COLOR, s=120, zorder=5, edgecolors=PAGE_BG, linewidth=0.8)\n\n# LCL annotation — lifted condensation level (base of cumulus clouds)\nax.axhline(y=P_lcl, color=INK_MUTED, linewidth=0.8, alpha=0.45, linestyle=\":\", zorder=3)\nax.text(48, P_lcl, f\"LCL {P_lcl:.0f}hPa\", fontsize=6.5, color=INK_MUTED, va=\"center\", ha=\"right\")\n\n# Style\nax.set_xlabel(\"Temperature (°C)\", fontsize=10, color=INK)\nax.set_ylabel(\"Pressure (hPa)\", fontsize=10, color=INK)\nax.set_title(\n    \"skewt-logp-atmospheric · python · matplotlib · anyplot.ai\", fontsize=12, fontweight=\"medium\", color=INK, pad=10\n)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor spine in (\"left\", \"bottom\"):\n    ax.spines[spine].set_color(INK_SOFT)\n\nax.yaxis.grid(True, alpha=0.12, linewidth=0.5, color=INK)\nax.xaxis.grid(True, alpha=0.08, linewidth=0.5, color=INK)\n\n# Legend\nlegend_elements = [\n    Line2D([0], [0], color=TEMP_COLOR, linewidth=2, label=\"Temperature\"),\n    Line2D([0], [0], color=DEWPOINT_COLOR, linewidth=2, linestyle=\"--\", label=\"Dewpoint\"),\n    Line2D([0], [0], color=INK_MUTED, linewidth=1, linestyle=\":\", alpha=0.65, label=\"Lifted Parcel\"),\n    Patch(facecolor=CAPE_COLOR, alpha=0.5, label=\"CAPE\"),\n    Patch(facecolor=CIN_COLOR, alpha=0.5, label=\"CIN\"),\n    Line2D([0], [0], color=\"#8B4513\", linewidth=0.8, alpha=0.6, label=\"Isotherms\"),\n    Line2D([0], [0], color=\"#228B22\", linewidth=0.8, alpha=0.6, linestyle=\"--\", label=\"Dry Adiabats\"),\n    Line2D([0], [0], color=\"#4169E1\", linewidth=0.8, alpha=0.6, linestyle=\"-.\", label=\"Moist Adiabats\"),\n    Line2D([0], [0], color=\"#9932CC\", linewidth=0.8, alpha=0.6, linestyle=\":\", label=\"Mixing Ratio\"),\n]\nleg = ax.legend(handles=legend_elements, loc=\"upper right\", fontsize=7, framealpha=0.9)\nleg.get_frame().set_facecolor(ELEVATED_BG)\nleg.get_frame().set_edgecolor(INK_SOFT)\nplt.setp(leg.get_texts(), color=INK_SOFT)\n\nfig.subplots_adjust(left=0.13, right=0.97, top=0.93, bottom=0.10)\n\n# Save\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}