|
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: 2025-11-03 <br>\n", |
26 | | - "Repository: <a href=\"https://github.com/lsst/tutorial-notebooks\">github.com/lsst/tutorial-notebooks</a> <br>" |
| 25 | + "Last verified to run: 2025-12-04 <br>\n", |
| 26 | + "Repository: [github.com/lsst/tutorial-notebooks](https://github.com/lsst/tutorial-notebooks)\\\n", |
| 27 | + "DOI: [10.11578/rubin/dc.20250909.20](https://doi.org/10.11578/rubin/dc.20250909.20)" |
27 | 28 | ] |
28 | 29 | }, |
29 | 30 | { |
|
42 | 43 | "Please consider acknowledging them if this notebook is used for the preparation of journal articles, software releases, or other notebooks.\n", |
43 | 44 | "\n", |
44 | 45 | "**Get Support:**\n", |
45 | | - "Everyone is encouraged to ask questions or raise issues in the \n", |
46 | | - "<a href=\"https://community.lsst.org/c/support\">Support Category</a> \n", |
47 | | - "of the Rubin Community Forum.\n", |
| 46 | + "Everyone is encouraged to ask questions or raise issues in the [Support Category](https://community.lsst.org/c/support) of the Rubin Community Forum.\n", |
48 | 47 | "Rubin staff will respond to all questions posted there." |
49 | 48 | ] |
50 | 49 | }, |
|
58 | 57 | "\n", |
59 | 58 | "This notebook performs forced photometry using the Data Preview 1 (DP1) data products on an input image, given a set of input IDs and RA/Dec coordinates. The `ForcedMeasurementDriverTask` serves as a convenience function, allowing users to measure sources without running a full-scale `pipetask` command or dealing with additional pipeline setup. This task can measure fluxes, shapes, and other properties at known source positions (e.g., from a pre-existing catalog) on a given image, without running source detection. (Note that for brevity we disable shape measurement in this notebook.)\n", |
60 | 59 | "\n", |
61 | | - "**Related tutorials:** 100-level Detect and measure sources" |
| 60 | + "**Related tutorials:** The 100-level tutorial on how to detect and measure sources." |
62 | 61 | ] |
63 | 62 | }, |
64 | 63 | { |
|
185 | 184 | "source": [ |
186 | 185 | "## 2. Load an image and table\n", |
187 | 186 | "\n", |
188 | | - "Identify coadded images overlapping a particular sky position and select one to run forced photometry on. Also load the corresponding `object` table using the TAP service so that forced photometry can be run at the positions of already-detected objects." |
| 187 | + "Identify coadded images overlapping a particular sky position and select one to run forced photometry on. Also load the corresponding `object` table using the TAP service so that forced photometry can be run at the positions of already-detected objects.\n", |
| 188 | + "\n", |
| 189 | + "Define the right ascension and declination as the center of the ECDFS field, and use the $r$ band." |
189 | 190 | ] |
190 | 191 | }, |
191 | 192 | { |
|
200 | 201 | "band = 'r'" |
201 | 202 | ] |
202 | 203 | }, |
| 204 | + { |
| 205 | + "cell_type": "markdown", |
| 206 | + "id": "35debe43-cd77-431b-a294-9e50a037738b", |
| 207 | + "metadata": {}, |
| 208 | + "source": [ |
| 209 | + "Execute a butler query for deep coadd images." |
| 210 | + ] |
| 211 | + }, |
203 | 212 | { |
204 | 213 | "cell_type": "code", |
205 | 214 | "execution_count": null, |
|
209 | 218 | "source": [ |
210 | 219 | "query = f\"band='{band}' AND \\\n", |
211 | 220 | " patch.region OVERLAPS POINT({ra}, {dec})\"\n", |
212 | | - "\n", |
213 | | - "coadd_img_refs = butler.query_datasets('deep_coadd', where=query)\n", |
214 | | - "\n", |
215 | | - "ref = coadd_img_refs[0]" |
| 221 | + "coadd_img_refs = butler.query_datasets('deep_coadd', where=query)" |
| 222 | + ] |
| 223 | + }, |
| 224 | + { |
| 225 | + "cell_type": "markdown", |
| 226 | + "id": "cc662eff-84ed-4fa9-9baa-7901cda08038", |
| 227 | + "metadata": {}, |
| 228 | + "source": [ |
| 229 | + "Assert that at least one matching image was returned, and retrieve the first in the list." |
216 | 230 | ] |
217 | 231 | }, |
218 | 232 | { |
219 | 233 | "cell_type": "code", |
220 | 234 | "execution_count": null, |
221 | | - "id": "b8e8b42d-eda2-4c96-a412-22e99da51a5d", |
| 235 | + "id": "7b1aa5b0-b59e-49c2-8891-f7d0d469851b", |
222 | 236 | "metadata": {}, |
223 | 237 | "outputs": [], |
224 | 238 | "source": [ |
| 239 | + "assert(len(coadd_img_refs) > 0)\n", |
| 240 | + "ref = coadd_img_refs[0]\n", |
225 | 241 | "exposure = butler.get(ref)" |
226 | 242 | ] |
227 | 243 | }, |
|
230 | 246 | "id": "3a57d04c-00ae-4278-b637-9e312084419d", |
231 | 247 | "metadata": {}, |
232 | 248 | "source": [ |
233 | | - "Extract a list of objects from the `Object` table in a small radius near the position of interest. These will be used both as an input list for forced measurement, and as a comparison for the results." |
| 249 | + "Query for point-like objects from the `Object` table in a 0.3 degree radius near the position of interest that overlap the deep coadd image's tract and patch. " |
234 | 250 | ] |
235 | 251 | }, |
236 | 252 | { |
|
243 | 259 | "query = f\"\"\"SELECT TOP 50 objectId, coord_ra, coord_dec, {band}_psfMag, {band}_psfFlux\n", |
244 | 260 | " FROM dp1.Object\n", |
245 | 261 | " WHERE CONTAINS(POINT('ICRS', coord_ra, coord_dec),\n", |
246 | | - " CIRCLE('ICRS', {ra}, {dec}, 0.1)) = 1\n", |
| 262 | + " CIRCLE('ICRS', {ra}, {dec}, 0.3)) = 1\n", |
247 | 263 | " AND refExtendedness < 0.5\n", |
248 | 264 | " AND {band}_psfMag < 26 AND {band}_psfMag > 17\n", |
249 | 265 | " AND tract = {ref.dataId['tract']}\n", |
|
254 | 270 | "job.wait(phases=['COMPLETED', 'ERROR'])\n", |
255 | 271 | "print('Job phase is', job.phase)\n", |
256 | 272 | "if job.phase == 'ERROR':\n", |
257 | | - " job.raise_if_error()\n", |
| 273 | + " job.raise_if_error()" |
| 274 | + ] |
| 275 | + }, |
| 276 | + { |
| 277 | + "cell_type": "markdown", |
| 278 | + "id": "f0939bcb-a517-4ec3-b2c0-3fb3b8700681", |
| 279 | + "metadata": {}, |
| 280 | + "source": [ |
| 281 | + "Retrieve the search results as `objtable`.\n", |
| 282 | + "This table will be used both as an input list for forced measurement, and as a comparison for the results." |
| 283 | + ] |
| 284 | + }, |
| 285 | + { |
| 286 | + "cell_type": "code", |
| 287 | + "execution_count": null, |
| 288 | + "id": "1803ae74-76d1-42f1-9e29-39231188f026", |
| 289 | + "metadata": {}, |
| 290 | + "outputs": [], |
| 291 | + "source": [ |
258 | 292 | "assert job.phase == 'COMPLETED'\n", |
259 | 293 | "objtable = job.fetch_result().to_table()" |
260 | 294 | ] |
261 | 295 | }, |
| 296 | + { |
| 297 | + "cell_type": "markdown", |
| 298 | + "id": "930f2a06-8918-43f5-9a05-bca95da38b03", |
| 299 | + "metadata": {}, |
| 300 | + "source": [ |
| 301 | + "Ensure that the table is not empty." |
| 302 | + ] |
| 303 | + }, |
| 304 | + { |
| 305 | + "cell_type": "code", |
| 306 | + "execution_count": null, |
| 307 | + "id": "f3234934-8c70-4a75-87b8-2b59e5c1eb1a", |
| 308 | + "metadata": {}, |
| 309 | + "outputs": [], |
| 310 | + "source": [ |
| 311 | + "assert(len(objtable) > 0)" |
| 312 | + ] |
| 313 | + }, |
262 | 314 | { |
263 | 315 | "cell_type": "markdown", |
264 | 316 | "id": "e4243773-e324-4415-884c-857cdb497688", |
|
434 | 486 | "source": [ |
435 | 487 | "The forced measurement functionality is limited to a handful of measurement plugins (for example, model-fitting algorithms will not work with this task because they require more ancillary information than it is set up to receive). Nonetheless, it offers a quick and easy way to extract measurements at arbitrary positions.\n", |
436 | 488 | "\n", |
437 | | - "### 3.1 Compare the forced measurements to the Object table\n", |
| 489 | + "### 3.1. Compare the forced measurements to the Object table\n", |
438 | 490 | "\n", |
439 | 491 | "Make a plot showing the ratio of the measured PSF fluxes from the forced measurement task to the `psfFlux` for the same objects in the `Object` table. Plot these as a function of magnitude, using Astropy units to convert `slot_PsfFlux_flux` from the `result` table to AB magnitudes." |
440 | 492 | ] |
|
468 | 520 | "source": [ |
469 | 521 | "## 4. Forced measurement on a visit image\n", |
470 | 522 | "\n", |
471 | | - "The forced measurement task can be run on any `ExposureF` object. To demonstrate this, retrieve a `visit_image` and execute the driver to extract forced photometry on that image. The appropriate input catalog for `visit_image` measurements is the `Source` table." |
| 523 | + "The forced measurement task can be run on any `ExposureF` object. To demonstrate this, retrieve a `visit_image` and execute the driver to extract forced photometry on that image. The appropriate input catalog for `visit_image` measurements is the `Source` table.\n", |
| 524 | + "\n", |
| 525 | + "Query the butler for overlapping visit images and retrieve the first in the list." |
472 | 526 | ] |
473 | 527 | }, |
474 | 528 | { |
|
480 | 534 | "source": [ |
481 | 535 | "query = f\"band='{band}' AND \\\n", |
482 | 536 | " visit.region OVERLAPS POINT({ra}, {dec})\"\n", |
483 | | - "\n", |
484 | 537 | "visit_img_refs = butler.query_datasets('visit_image', where=query)\n", |
485 | | - "\n", |
486 | | - "ref = visit_img_refs[0]" |
| 538 | + "assert(len(visit_img_refs) > 0)\n", |
| 539 | + "ref = visit_img_refs[0]\n", |
| 540 | + "exposure = butler.get(ref)" |
487 | 541 | ] |
488 | 542 | }, |
489 | 543 | { |
490 | | - "cell_type": "code", |
491 | | - "execution_count": null, |
492 | | - "id": "171e7626-85a7-4974-b1e6-d4964f8b814b", |
| 544 | + "cell_type": "markdown", |
| 545 | + "id": "c0760743-4c48-4db1-95fb-b2d2dd05da30", |
493 | 546 | "metadata": {}, |
494 | | - "outputs": [], |
495 | 547 | "source": [ |
496 | | - "exposure = butler.get(ref)" |
| 548 | + "Query the TAP service for point-like sources detected in the visit image." |
497 | 549 | ] |
498 | 550 | }, |
499 | 551 | { |
|
506 | 558 | "query = f\"\"\"SELECT TOP 50 sourceId, ra, dec, psfFlux\n", |
507 | 559 | " FROM dp1.Source\n", |
508 | 560 | " WHERE CONTAINS(POINT('ICRS', ra, dec),\n", |
509 | | - " CIRCLE('ICRS', {ra}, {dec}, 0.1)) = 1\n", |
| 561 | + " CIRCLE('ICRS', {ra}, {dec}, 0.3)) = 1\n", |
510 | 562 | " AND extendedness < 0.5\n", |
511 | 563 | " AND visit = {ref.dataId['visit']}\n", |
512 | 564 | " AND detector = {ref.dataId['detector']}\"\"\"\n", |
|
516 | 568 | "job.wait(phases=['COMPLETED', 'ERROR'])\n", |
517 | 569 | "print('Job phase is', job.phase)\n", |
518 | 570 | "if job.phase == 'ERROR':\n", |
519 | | - " job.raise_if_error()\n", |
| 571 | + " job.raise_if_error()" |
| 572 | + ] |
| 573 | + }, |
| 574 | + { |
| 575 | + "cell_type": "code", |
| 576 | + "execution_count": null, |
| 577 | + "id": "5ebe2ed5-1b67-4ad8-b798-d69726dfba4e", |
| 578 | + "metadata": {}, |
| 579 | + "outputs": [], |
| 580 | + "source": [ |
520 | 581 | "assert job.phase == 'COMPLETED'\n", |
521 | | - "srctable = job.fetch_result().to_table()" |
| 582 | + "srctable = job.fetch_result().to_table()\n", |
| 583 | + "assert(len(srctable) > 0)" |
| 584 | + ] |
| 585 | + }, |
| 586 | + { |
| 587 | + "cell_type": "markdown", |
| 588 | + "id": "acc39c47-eb40-46a9-b130-c2873d14a41f", |
| 589 | + "metadata": {}, |
| 590 | + "source": [ |
| 591 | + "Configure and run forced photometry." |
522 | 592 | ] |
523 | 593 | }, |
524 | 594 | { |
|
588 | 658 | "id": "af970fba-e2b5-42db-95a5-b24e6ec11caa", |
589 | 659 | "metadata": {}, |
590 | 660 | "source": [ |
591 | | - "### 4.1 Compare the forced measurements to the Source table\n", |
| 661 | + "### 4.1. Compare the forced measurements to the Source table\n", |
592 | 662 | "\n", |
593 | 663 | "Make a plot showing the ratio of the measured PSF fluxes from the forced measurement task to the `psfFlux` for the same objects in the `Source` table. Plot these as a function of magnitude, using Astropy units to convert `slot_PsfFlux_flux` from the `result` table to AB magnitudes." |
594 | 664 | ] |
|
0 commit comments