|
22 | 22 | "Data Release: <a href=\"https://dp1.lsst.io\">Data Preview 1</a> <br>\n", |
23 | 23 | "Container Size: large <br>\n", |
24 | 24 | "LSST Science Pipelines version: r29.2.0 <br>\n", |
25 | | - "Last verified to run: 2026-02-20 <br>\n", |
| 25 | + "Last verified to run: 2026-02-23 <br>\n", |
26 | 26 | "Repository: <a href=\"https://github.com/lsst/tutorial-notebooks\">github.com/lsst/tutorial-notebooks</a> <br>\n", |
27 | 27 | "DOI: <a href=\"https://doi.org/10.11578/rubin/dc.20250909.20\">10.11578/rubin/dc.20250909.20</a> <br>" |
28 | 28 | ] |
|
90 | 90 | "metadata": {}, |
91 | 91 | "outputs": [], |
92 | 92 | "source": [ |
| 93 | + "import os\n", |
93 | 94 | "import numpy as np\n", |
94 | 95 | "import matplotlib.pyplot as plt\n", |
95 | 96 | "from matplotlib.colors import LogNorm\n", |
96 | | - "from astropy.coordinates import SkyCoord, Galactocentric\n", |
97 | | - "import astropy.units as u\n", |
98 | | - "import os\n", |
| 97 | + "from matplotlib.patches import Polygon, Rectangle\n", |
| 98 | + "from matplotlib.path import Path\n", |
99 | 99 | "\n", |
100 | 100 | "from lsst.rsp import get_tap_service\n", |
101 | 101 | "from rubin_sim.phot_utils import DustValues" |
|
347 | 347 | "\n", |
348 | 348 | "for ax, (x, y, xl, yl) in zip(axes, data_configs):\n", |
349 | 349 | " hb = ax.hexbin(x, y, gridsize=200, extent=[-1, 2, 15, 27], \n", |
350 | | - " cmap='viridis', norm=LogNorm(vmax=70))\n", |
| 350 | + " norm=LogNorm(vmax=70))\n", |
351 | 351 | " ax.set_xlim(-1, 2)\n", |
352 | 352 | " ax.set_ylim(27, 15)\n", |
353 | 353 | " ax.set_xlabel(xl)\n", |
|
376 | 376 | "id": "191a8d76-a52e-4116-9798-ee2fe8e70383", |
377 | 377 | "metadata": {}, |
378 | 378 | "source": [ |
379 | | - "## 3. Spatial distribution of stellar populations\n", |
| 379 | + "## 3. Stars at distinct evolutionary stages \n", |
| 380 | + "\n", |
| 381 | + "### 3.1. Identify stellar populations\n", |
| 382 | + "Identify RGB, RC, HB, MSTO, BP, and MW stars in the CMD." |
| 383 | + ] |
| 384 | + }, |
| 385 | + { |
| 386 | + "cell_type": "code", |
| 387 | + "execution_count": null, |
| 388 | + "id": "a56900f5-f4d1-4d80-a5fd-c7749b0b783e", |
| 389 | + "metadata": {}, |
| 390 | + "outputs": [], |
| 391 | + "source": [ |
| 392 | + "phase_colors = {\n", |
| 393 | + " 'MSTO': '#1f77b4', 'YMS': '#aec7e8', 'RGB': '#d62728', \n", |
| 394 | + " 'RC': '#ff7f0e', 'HB': '#2ca02c', 'AGBb': '#9467bd'\n", |
| 395 | + "}\n", |
380 | 396 | "\n", |
381 | | - "Select RGB, RC, HB, MSTO, BP, and MW stars from the CMD and look at their spatial distribution." |
| 397 | + "regions = [\n", |
| 398 | + " {'type': 'rect', 'label': 'MSTO',\n", |
| 399 | + " 'xy': (0.0, 24.0), 'width': 0.5, 'height': 1.0, 'edgecolor': phase_colors['MSTO']},\n", |
| 400 | + " {'type': 'rect', 'label': 'YMSe',\n", |
| 401 | + " 'xy': (-0.25, 22.0), 'width': 0.25, 'height': 2.0, 'edgecolor': phase_colors['YMS']},\n", |
| 402 | + " {'type': 'poly', 'label': 'RGB',\n", |
| 403 | + " 'xy': [(1.25, 18.0), (1.0, 18.0), (0.65, 20.0), (0.35, 23.5), (0.65, 23.5), (0.8, 20.0)],\n", |
| 404 | + " 'edgecolor': phase_colors['RGB']},\n", |
| 405 | + " {'type': 'rect', 'label': 'RC',\n", |
| 406 | + " 'xy': (-0.2, 20.5), 'width': 0.9, 'height': 1.1, 'edgecolor': phase_colors['RC']},\n", |
| 407 | + " {'type': 'rect', 'label': 'HB',\n", |
| 408 | + " 'xy': (-0.2, 20.5), 'width': 0.9, 'height': 1.1, 'edgecolor': phase_colors['HB']},\n", |
| 409 | + " {'type': 'rect', 'label': 'AGBb',\n", |
| 410 | + " 'xy': (-0.2, 20.5), 'width': 0.9, 'height': 1.1, 'edgecolor': phase_colors['AGBb']},\n", |
| 411 | + "]" |
| 412 | + ] |
| 413 | + }, |
| 414 | + { |
| 415 | + "cell_type": "code", |
| 416 | + "execution_count": null, |
| 417 | + "id": "18ef5a94-105a-434a-a8c1-1dc609ff2ae7", |
| 418 | + "metadata": {}, |
| 419 | + "outputs": [], |
| 420 | + "source": [ |
| 421 | + "fig, ax = plt.subplots(figsize=(8, 6))\n", |
| 422 | + "hb = ax.hexbin(df['g_psfMag0'] - df['i_psfMag0'], df['i_psfMag0'],\n", |
| 423 | + " gridsize=200, extent=[-1, 2, 15, 27], norm=LogNorm(vmax=70))\n", |
| 424 | + "ax.set_xlim(-1, 2)\n", |
| 425 | + "ax.set_ylim(27, 15)\n", |
| 426 | + "ax.set_xlabel(r'$g_0 - i_0$')\n", |
| 427 | + "ax.set_ylabel(r'$i_0$')\n", |
| 428 | + "ax.set_title('CMD with Stellar Phase Boxes')\n", |
| 429 | + "\n", |
| 430 | + "legend_elements = []\n", |
| 431 | + "for reg in regions:\n", |
| 432 | + " if reg['type'] == 'rect':\n", |
| 433 | + " patch = Rectangle(reg['xy'], reg['width'], reg['height'], \n", |
| 434 | + " fill=False, lw=2, edgecolor=reg['edgecolor'], label=reg['label'])\n", |
| 435 | + " elif reg['type'] == 'poly':\n", |
| 436 | + " patch = Polygon(reg['xy'], fill=False, lw=2, edgecolor=reg['edgecolor'], label=reg['label'])\n", |
| 437 | + " \n", |
| 438 | + " ax.add_patch(patch)\n", |
| 439 | + " legend_elements.append(patch)\n", |
| 440 | + "\n", |
| 441 | + "ax.legend(handles=legend_elements, loc='upper left', title='Stellar Phases')\n", |
| 442 | + "\n", |
| 443 | + "plt.show()" |
| 444 | + ] |
| 445 | + }, |
| 446 | + { |
| 447 | + "cell_type": "markdown", |
| 448 | + "id": "00c73f02-3440-454e-8b36-91e4dc0ee998", |
| 449 | + "metadata": {}, |
| 450 | + "source": [ |
| 451 | + "### 3.2. Tag stars with evolutionary phase\n", |
| 452 | + "\n", |
| 453 | + "Define two functions to select stars within either a specified 2D polygon or a rectangular region." |
| 454 | + ] |
| 455 | + }, |
| 456 | + { |
| 457 | + "cell_type": "code", |
| 458 | + "execution_count": null, |
| 459 | + "id": "bf90a560-eecc-4c67-ac85-c6b786682e3d", |
| 460 | + "metadata": {}, |
| 461 | + "outputs": [], |
| 462 | + "source": [ |
| 463 | + "def get_poly_mask(color, mag, vertices):\n", |
| 464 | + " \"\"\"\n", |
| 465 | + " Generate a boolean mask for points falling within a specified 2D polygon.\n", |
| 466 | + "\n", |
| 467 | + " Parameters\n", |
| 468 | + " ----------\n", |
| 469 | + " color : array-like\n", |
| 470 | + " 1D array of stellar color indices (e.g., g - i).\n", |
| 471 | + " mag : array-like\n", |
| 472 | + " 1D array of stellar magnitudes (e.g., i).\n", |
| 473 | + " vertices : list of tuples\n", |
| 474 | + " A list of (x, y) coordinates defining the polygon vertices in the \n", |
| 475 | + " (color, mag) plane.\n", |
| 476 | + "\n", |
| 477 | + " Returns\n", |
| 478 | + " -------\n", |
| 479 | + " mask : numpy.ndarray\n", |
| 480 | + " A boolean array where True indicates the point is inside the polygon.\n", |
| 481 | + " \"\"\"\n", |
| 482 | + " path = Path(vertices)\n", |
| 483 | + " points = np.column_stack((color, mag))\n", |
| 484 | + " \n", |
| 485 | + " return path.contains_points(points)\n", |
| 486 | + "\n", |
| 487 | + "def get_rect_mask(color, mag, x_min, y_min, width, height):\n", |
| 488 | + " \"\"\"\n", |
| 489 | + " Generate a boolean mask for points within a rectangular region.\n", |
| 490 | + "\n", |
| 491 | + " Parameters\n", |
| 492 | + " ----------\n", |
| 493 | + " color : array-like\n", |
| 494 | + " 1D array of stellar color indices.\n", |
| 495 | + " mag : array-like\n", |
| 496 | + " 1D array of stellar magnitudes.\n", |
| 497 | + " x_min : float\n", |
| 498 | + " The minimum color value.\n", |
| 499 | + " y_min : float\n", |
| 500 | + " The minimum magnitude value.\n", |
| 501 | + " width : float\n", |
| 502 | + " The extent of the box along the color axis.\n", |
| 503 | + " height : float\n", |
| 504 | + " The extent of the box along the magnitude axis.\n", |
| 505 | + "\n", |
| 506 | + " Returns\n", |
| 507 | + " -------\n", |
| 508 | + " mask : numpy.ndarray\n", |
| 509 | + " A boolean array where True indicates the point is inside the rectangle.\n", |
| 510 | + " \"\"\"\n", |
| 511 | + " return (color >= x_min) & (color <= x_min + width) & \\\n", |
| 512 | + " (mag >= y_min) & (mag <= y_min + height)" |
| 513 | + ] |
| 514 | + }, |
| 515 | + { |
| 516 | + "cell_type": "markdown", |
| 517 | + "id": "e3ff286e-13fe-4667-9cd4-e9d28540de71", |
| 518 | + "metadata": {}, |
| 519 | + "source": [ |
| 520 | + "Apply selections based on the \"regions\" list defined previously, assign label to the mask and add a categorical column to the `df` table. " |
| 521 | + ] |
| 522 | + }, |
| 523 | + { |
| 524 | + "cell_type": "code", |
| 525 | + "execution_count": null, |
| 526 | + "id": "497ade76-f56e-4159-bee9-2af14d87a764", |
| 527 | + "metadata": {}, |
| 528 | + "outputs": [], |
| 529 | + "source": [ |
| 530 | + "df['phase_label'] = 'Field/Unclassified'\n", |
| 531 | + "\n", |
| 532 | + "for reg in regions:\n", |
| 533 | + " if reg['type'] == 'rect':\n", |
| 534 | + " mask = get_rect_mask(df['g_psfMag0'] - df['i_psfMag0'], df['i_psfMag0'],\n", |
| 535 | + " reg['xy'][0], reg['xy'][1], reg['width'], reg['height'])\n", |
| 536 | + " elif reg['type'] == 'poly':\n", |
| 537 | + " mask = get_poly_mask(df['g_psfMag0'] - df['i_psfMag0'], df['i_psfMag0'], reg['xy'])\n", |
| 538 | + " \n", |
| 539 | + " df.loc[mask, 'phase_label'] = reg['label']\n", |
| 540 | + "\n", |
| 541 | + "print(df['phase_label'].value_counts())" |
382 | 542 | ] |
383 | 543 | }, |
384 | 544 | { |
385 | 545 | "cell_type": "markdown", |
386 | 546 | "id": "b3d02274-a33f-4d8f-ac6d-d3bb429455c0", |
387 | 547 | "metadata": {}, |
388 | 548 | "source": [ |
389 | | - "## 4. Stellar distribution in CCDs " |
| 549 | + "## 4. Multi-Diagnostic Visualization\n", |
| 550 | + "\n", |
| 551 | + "### 4.1. Stellar distribution in CCDs \n", |
| 552 | + "\n", |
| 553 | + "Visualize how these CMD-selected populations behave in the Color-Color space ($g-r$ vs. $r-i$)." |
390 | 554 | ] |
391 | 555 | }, |
392 | 556 | { |
393 | 557 | "cell_type": "code", |
394 | 558 | "execution_count": null, |
| 559 | + "id": "b46f40a4-fd13-44de-ae60-0a8851e202d6", |
| 560 | + "metadata": {}, |
| 561 | + "outputs": [], |
| 562 | + "source": [ |
| 563 | + "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6), sharey=True)\n", |
| 564 | + "\n", |
| 565 | + "gr = df['g_psfMag0'] - df['r_psfMag0']\n", |
| 566 | + "gi = df['g_psfMag0'] - df['r_psfMag0']\n", |
| 567 | + "ri = df['r_psfMag0'] - df['i_psfMag0']\n", |
| 568 | + "\n", |
| 569 | + "for phase, color in phase_colors.items():\n", |
| 570 | + " subset = df[df['phase_label'] == phase]\n", |
| 571 | + " if subset.empty: continue\n", |
| 572 | + " \n", |
| 573 | + " ax1.scatter(gr.loc[subset.index], ri.loc[subset.index], \n", |
| 574 | + " s=5, color=color, label=phase, alpha=0.6)\n", |
| 575 | + " \n", |
| 576 | + " ax2.scatter(gi.loc[subset.index], ri.loc[subset.index],\n", |
| 577 | + " s=5, color=color, label=phase, alpha=0.6)\n", |
| 578 | + "\n", |
| 579 | + "ax1.set_xlabel('$g_0 - r_0$')\n", |
| 580 | + "ax1.set_ylabel('$r_0 - i_0$')\n", |
| 581 | + "ax1.legend(title=\"CMD-Selected Phases\", markerscale=2)\n", |
| 582 | + "# ax1.grid(alpha=0.3)\n", |
| 583 | + "\n", |
| 584 | + "ax2.set_xlabel('$g_0 - i_0$')\n", |
| 585 | + "\n", |
| 586 | + "plt.show()" |
| 587 | + ] |
| 588 | + }, |
| 589 | + { |
| 590 | + "cell_type": "markdown", |
395 | 591 | "id": "bb9d8d89-4ef9-44fb-8a59-378fb42c5b77", |
396 | 592 | "metadata": {}, |
| 593 | + "source": [ |
| 594 | + "### 4.2. Spatial distribution of distinct populations\n", |
| 595 | + "\n", |
| 596 | + "Explore spatial distribution of distinct populations within the field footprint. Note that lack of stars in the inner region results from skip of deblending process there in the LSST Science pipelines due to crowding." |
| 597 | + ] |
| 598 | + }, |
| 599 | + { |
| 600 | + "cell_type": "code", |
| 601 | + "execution_count": null, |
| 602 | + "id": "ab5d84ec-6cac-4376-9494-50878060513c", |
| 603 | + "metadata": {}, |
397 | 604 | "outputs": [], |
398 | | - "source": [] |
| 605 | + "source": [ |
| 606 | + "fig, ax = plt.subplots(figsize=(8, 8))\n", |
| 607 | + "\n", |
| 608 | + "# Plot unclassified stars in the background\n", |
| 609 | + "ax.scatter(df.loc[df['phase'] == 'unclassified', 'ra'], \n", |
| 610 | + " df.loc[df['phase'] == 'unclassified', 'dec'], \n", |
| 611 | + " s=1, color='lightgrey', alpha=0.3)\n", |
| 612 | + "\n", |
| 613 | + "for phase, color in phase_colors.items():\n", |
| 614 | + " subset = df[df['phase'] == phase]\n", |
| 615 | + " if not subset.empty:\n", |
| 616 | + " ax.scatter(subset['ra'], subset['dec'], s=5, \n", |
| 617 | + " color=color, label=f\"{phase} (N={len(subset)})\")\n", |
| 618 | + "\n", |
| 619 | + "ax.set_xlabel('RA [deg]')\n", |
| 620 | + "ax.set_ylabel('Dec [deg]')\n", |
| 621 | + "ax.set_title('Spatial Distribution of Stellar Populations')\n", |
| 622 | + "ax.legend(markerscale=3, loc='upper right')\n", |
| 623 | + "ax.invert_xaxis()\n", |
| 624 | + "\n", |
| 625 | + "plt.show()" |
| 626 | + ] |
399 | 627 | } |
400 | 628 | ], |
401 | 629 | "metadata": { |
|
0 commit comments