{"spec_id":"lightcurve-transit","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nlightcurve-transit: Astronomical Light Curve\nLibrary: plotnine 0.15.7 | Python 3.13.14\nQuality: 89/100 | Updated: 2026-06-20\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent self-import: this file is named after the library it imports.\n# Strip the script directory so Python finds the installed package instead.\nsys.path = [p for p in sys.path if p and os.path.abspath(p) != os.path.dirname(os.path.abspath(__file__))]\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    annotate,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_line,\n    geom_linerange,\n    geom_point,\n    geom_ribbon,\n    ggplot,\n    guide_legend,\n    guides,\n    labs,\n    scale_color_manual,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Imprint palette — positions 1–8\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Theme-adaptive chrome\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\n# Data\nnp.random.seed(42)\nn_points = 400\nphase = np.sort(np.random.uniform(0.0, 1.0, n_points))\n\ntransit_center = 0.5\ntransit_half_dur = 0.04\ningress_half = 0.01\ntransit_depth = 0.012\nu1, u2 = 0.4, 0.1\n\n# Transit model — observation grid (vectorized)\ndist = np.abs(phase - transit_center)\nfull_transit = dist < transit_half_dur - ingress_half\ningress = (dist >= transit_half_dur - ingress_half) & (dist < transit_half_dur + ingress_half)\nr = np.clip(dist / transit_half_dur, 0, 1)\nmu = np.sqrt(np.maximum(1 - r**2, 0))\nlimb = 1 - u1 * (1 - mu) - u2 * (1 - mu) ** 2\ntransit_model = np.ones(n_points)\ntransit_model[full_transit] = 1.0 - transit_depth * limb[full_transit]\nfrac = (transit_half_dur + ingress_half - dist[ingress]) / (2 * ingress_half)\nfrac = 3 * frac**2 - 2 * frac**3\ntransit_model[ingress] = 1.0 - transit_depth * limb[ingress] * frac\n\nflux_err = np.random.uniform(0.0008, 0.0025, n_points)\nflux = transit_model + np.random.normal(0, 1, n_points) * flux_err\n\n# Fine grid for smooth model overlay\nphase_fine = np.linspace(0.0, 1.0, 2000)\ndist_f = np.abs(phase_fine - transit_center)\nfull_f = dist_f < transit_half_dur - ingress_half\ning_f = (dist_f >= transit_half_dur - ingress_half) & (dist_f < transit_half_dur + ingress_half)\nr_f = np.clip(dist_f / transit_half_dur, 0, 1)\nmu_f = np.sqrt(np.maximum(1 - r_f**2, 0))\nlimb_f = 1 - u1 * (1 - mu_f) - u2 * (1 - mu_f) ** 2\nmodel_fine = np.ones(2000)\nmodel_fine[full_f] = 1.0 - transit_depth * limb_f[full_f]\nfrac_f = (transit_half_dur + ingress_half - dist_f[ing_f]) / (2 * ingress_half)\nfrac_f = 3 * frac_f**2 - 2 * frac_f**3\nmodel_fine[ing_f] = 1.0 - transit_depth * limb_f[ing_f] * frac_f\n\nmodel_upper = model_fine + 0.0012\nmodel_lower = model_fine - 0.0012\nnear_transit = np.abs(phase_fine - transit_center) < transit_half_dur + ingress_half + 0.02\n\ndf_obs = pd.DataFrame({\"phase\": phase, \"flux\": flux, \"flux_err\": flux_err, \"series\": \"Observations\"})\ndf_model = pd.DataFrame({\"phase\": phase_fine, \"flux\": model_fine, \"series\": \"Transit Model\"})\ndf_ribbon = pd.DataFrame(\n    {\"phase\": phase_fine[near_transit], \"upper\": model_upper[near_transit], \"lower\": model_lower[near_transit]}\n)\n\nmin_model = model_fine.min()\ndepth_pct = (1.0 - min_model) * 100\n\n# Plot\nplot = (\n    ggplot()\n    + geom_hline(yintercept=1.0, color=INK_MUTED, size=0.4, linetype=\"dotted\", alpha=0.7)\n    + geom_ribbon(aes(x=\"phase\", ymin=\"lower\", ymax=\"upper\"), data=df_ribbon, fill=IMPRINT_PALETTE[1], alpha=0.18)\n    + geom_linerange(\n        aes(x=\"phase\", ymin=\"flux - flux_err\", ymax=\"flux + flux_err\"),\n        data=df_obs,\n        color=IMPRINT_PALETTE[0],\n        alpha=0.30,\n        size=0.25,\n    )\n    + geom_point(aes(x=\"phase\", y=\"flux\", color=\"series\"), data=df_obs, alpha=0.65, size=1.5, stroke=0)\n    + geom_line(aes(x=\"phase\", y=\"flux\", color=\"series\"), data=df_model, size=1.2)\n    + scale_color_manual(values={\"Observations\": IMPRINT_PALETTE[0], \"Transit Model\": IMPRINT_PALETTE[1]})\n    + guides(color=guide_legend(title=None))\n    + annotate(\n        \"text\",\n        x=0.63,\n        y=min_model - 0.0008,\n        label=f\"Depth: {depth_pct:.2f}%\",\n        size=3.0,\n        color=INK_MUTED,\n        fontstyle=\"italic\",\n    )\n    + annotate(\n        \"segment\", x=0.54, xend=0.54, y=1.0, yend=min_model, color=INK_MUTED, size=0.5, linetype=\"dashed\", alpha=0.7\n    )\n    + annotate(\"text\", x=0.08, y=1.0005, label=\"Baseline\", size=2.5, color=INK_MUTED, fontstyle=\"italic\")\n    + labs(x=\"Orbital Phase\", y=\"Relative Flux\", title=\"lightcurve-transit · python · plotnine · anyplot.ai\")\n    + scale_x_continuous(breaks=np.arange(0, 1.1, 0.1))\n    + scale_y_continuous(labels=lambda lst: [f\"{v:.3f}\" for v in lst])\n    + theme_minimal()\n    + theme(\n        figure_size=(8, 4.5),\n        plot_title=element_text(size=12, weight=\"bold\", margin={\"b\": 6}, color=INK),\n        axis_title=element_text(size=10, color=INK),\n        axis_text=element_text(size=8, color=INK_SOFT),\n        legend_text=element_text(size=8, color=INK_SOFT),\n        legend_title=element_blank(),\n        legend_position=(0.85, 0.95),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_key=element_rect(fill=PAGE_BG, color=\"none\"),\n        panel_grid_major_x=element_blank(),\n        panel_grid_minor=element_blank(),\n        panel_grid_major_y=element_line(color=INK, size=0.25, alpha=0.12),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        axis_line=element_line(color=INK_SOFT, size=0.4),\n    )\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\", verbose=False)\n"}