|
18 | 18 | "For the Rubin Science Platform at data.lsst.cloud. <br>\n", |
19 | 19 | "Data Release: <a href=\"https://dp1.lsst.io/\">Data Preview 1</a> <br>\n", |
20 | 20 | "Container Size: large <br>\n", |
21 | | - "LSST Science Pipelines version: Release r29.1.1 <br>\n", |
| 21 | + "LSST Science Pipelines version: r29.1.1 <br>\n", |
22 | 22 | "Last verified to run: 2025-06-20 <br>\n", |
23 | 23 | "Repository: <a href=\"https://github.com/lsst/tutorial-notebooks\">github.com/lsst/tutorial-notebooks</a> <br>" |
24 | 24 | ] |
|
30 | 30 | "source": [ |
31 | 31 | "**Learning objective:** Learn how to extract and plot a light curve for a variable star from DP1.\n", |
32 | 32 | "\n", |
33 | | - "**LSST data products:** `ForcedSourceOnDiaObject`, `Visit`\n", |
| 33 | + "**LSST data products:** `ForcedSourceOnDiaObject`, `DiaObject`, `Visit`\n", |
34 | 34 | "\n", |
35 | 35 | "**Packages:** `matplotlib`, `numpy`, `lsst.rsp`, `lsst.utils.plotting`\n", |
36 | 36 | "\n", |
|
156 | 156 | "\n", |
157 | 157 | "The units used to plot the light curve are in flux (nJy) and not magnitudes since the negative flux measurements would be omitted when converting to magnitudes.\n", |
158 | 158 | "\n", |
| 159 | + "### 2.1 Look up diaObjectId from coordinates\n", |
| 160 | + "\n", |
159 | 161 | "The first step to obtain light curve data is to define the coordinates (ra and dec) of a variable star captured in DP1: Gaia DR3 2912281258855051520." |
160 | 162 | ] |
161 | 163 | }, |
162 | 164 | { |
163 | 165 | "cell_type": "code", |
164 | 166 | "execution_count": null, |
165 | | - "id": "35077a54-59bd-44fa-9d31-114e3ddbd578", |
| 167 | + "id": "2b4fc159-bca1-40e8-b9b1-58b39a151bf8", |
166 | 168 | "metadata": {}, |
167 | 169 | "outputs": [], |
168 | 170 | "source": [ |
|
172 | 174 | }, |
173 | 175 | { |
174 | 176 | "cell_type": "markdown", |
175 | | - "id": "1f486e43-cbbe-4cab-b131-27eadc69c3b5", |
| 177 | + "id": "2d09a784-726c-4723-9df6-2f6ad99f6396", |
176 | 178 | "metadata": {}, |
177 | 179 | "source": [ |
178 | 180 | "Define a 0.5 arcsecond search radius, and convert it to degrees." |
|
181 | 183 | { |
182 | 184 | "cell_type": "code", |
183 | 185 | "execution_count": null, |
184 | | - "id": "fe86ad87-b4ee-45cc-ad6c-71d8537c249e", |
| 186 | + "id": "29a7fea7-0329-419d-af1f-fbd9b5e9eb4f", |
185 | 187 | "metadata": {}, |
186 | 188 | "outputs": [], |
187 | 189 | "source": [ |
|
190 | 192 | }, |
191 | 193 | { |
192 | 194 | "cell_type": "markdown", |
193 | | - "id": "3154ec3a-cd86-45ad-b215-0946624902b7", |
| 195 | + "id": "ba3dc2e2-aa27-49bb-a4de-446538973955", |
| 196 | + "metadata": {}, |
| 197 | + "source": [ |
| 198 | + "Search at this position in the `DiaObject` table to retrieve the `diaObjectId` of the corresponding object." |
| 199 | + ] |
| 200 | + }, |
| 201 | + { |
| 202 | + "cell_type": "code", |
| 203 | + "execution_count": null, |
| 204 | + "id": "11e3579b-6812-479a-916b-fdad65e01baa", |
| 205 | + "metadata": {}, |
| 206 | + "outputs": [], |
| 207 | + "source": [ |
| 208 | + "query = \"SELECT ra, dec, diaObjectId \"\\\n", |
| 209 | + " \"FROM dp1.DiaObject \"\\\n", |
| 210 | + " \"WHERE CONTAINS(POINT('ICRS', ra, dec), \"\\\n", |
| 211 | + " \"CIRCLE('ICRS', \" + str(ra_var) + \", \"\\\n", |
| 212 | + " + str(dec_var) + \", \" + str(search_rad) + \")) = 1\"\n", |
| 213 | + "print(query)" |
| 214 | + ] |
| 215 | + }, |
| 216 | + { |
| 217 | + "cell_type": "code", |
| 218 | + "execution_count": null, |
| 219 | + "id": "08a4d843-d206-4f35-b635-e4f9253603d7", |
| 220 | + "metadata": {}, |
| 221 | + "outputs": [], |
| 222 | + "source": [ |
| 223 | + "job = service.submit_job(query)\n", |
| 224 | + "job.run()\n", |
| 225 | + "job.wait(phases=['COMPLETED', 'ERROR'])\n", |
| 226 | + "print('Job phase is', job.phase)\n", |
| 227 | + "if job.phase == 'ERROR':\n", |
| 228 | + " job.raise_if_error()" |
| 229 | + ] |
| 230 | + }, |
| 231 | + { |
| 232 | + "cell_type": "markdown", |
| 233 | + "id": "f14304ef-f1b2-4d2e-9b2f-c8099a592cc8", |
| 234 | + "metadata": { |
| 235 | + "execution": { |
| 236 | + "iopub.execute_input": "2025-06-30T22:25:25.363571Z", |
| 237 | + "iopub.status.busy": "2025-06-30T22:25:25.362713Z", |
| 238 | + "iopub.status.idle": "2025-06-30T22:25:25.368671Z", |
| 239 | + "shell.execute_reply": "2025-06-30T22:25:25.367868Z", |
| 240 | + "shell.execute_reply.started": "2025-06-30T22:25:25.363544Z" |
| 241 | + } |
| 242 | + }, |
| 243 | + "source": [ |
| 244 | + "Fetch the results of the TAP search in table form and print the unique entries for `diaObjectId` in the output table." |
| 245 | + ] |
| 246 | + }, |
| 247 | + { |
| 248 | + "cell_type": "code", |
| 249 | + "execution_count": null, |
| 250 | + "id": "9b7285dd-4b63-4482-baf0-8f63650f8834", |
| 251 | + "metadata": {}, |
| 252 | + "outputs": [], |
| 253 | + "source": [ |
| 254 | + "assert job.phase == 'COMPLETED'\n", |
| 255 | + "DiaObj = job.fetch_result().to_table()\n", |
| 256 | + "print(np.unique(DiaObj['diaObjectId']))" |
| 257 | + ] |
| 258 | + }, |
| 259 | + { |
| 260 | + "cell_type": "markdown", |
| 261 | + "id": "4f3fcea5-957d-4984-8f93-e49e1970e48b", |
194 | 262 | "metadata": {}, |
195 | 263 | "source": [ |
196 | | - "Perform a TAP search on the coordinates of the transient in the `ForcedSourceOnDiaObject` table.\n", |
| 264 | + "### 2.2. Search based on diaObjectId\n", |
197 | 265 | "\n", |
198 | | - "Use a table JOIN with the `Visit` table to extract visit timing info (`expMidptMJD`) for each entry in the `ForcedSourceOnDiaObject`." |
| 266 | + "Provide this `diaObjectId` as a constraint to a TAP query to obtain the time series from `ForcedSourceOnDiaObject`. The use of an ID in this manner is always the recommended way to access data from a ForcedSource table." |
199 | 267 | ] |
200 | 268 | }, |
201 | 269 | { |
202 | 270 | "cell_type": "code", |
203 | 271 | "execution_count": null, |
204 | | - "id": "aee30c7f-38cf-4637-a7e9-dd476a69ddbc", |
| 272 | + "id": "0ca36902-76c6-4acd-a02b-c7e609f2cb82", |
205 | 273 | "metadata": {}, |
206 | 274 | "outputs": [], |
207 | 275 | "source": [ |
208 | | - "query = \"SELECT fsodo.diaObjectId, fsodo.coord_ra, fsodo.coord_dec, \"\\\n", |
209 | | - " \"fsodo.visit, fsodo.detector, fsodo.band, \"\\\n", |
| 276 | + "DiaObjID = DiaObj['diaObjectId'][0]" |
| 277 | + ] |
| 278 | + }, |
| 279 | + { |
| 280 | + "cell_type": "code", |
| 281 | + "execution_count": null, |
| 282 | + "id": "3fb7afdc-9709-4d8e-a3e6-fa46075fe0ef", |
| 283 | + "metadata": {}, |
| 284 | + "outputs": [], |
| 285 | + "source": [ |
| 286 | + "query = \"SELECT fsodo.coord_ra, fsodo.coord_dec, \"\\\n", |
| 287 | + " \"fsodo.diaObjectId, fsodo.visit, fsodo.band, \"\\\n", |
210 | 288 | " \"fsodo.psfDiffFlux, fsodo.psfDiffFluxErr, \"\\\n", |
211 | 289 | " \"fsodo.psfFlux as psfFlux, fsodo.psfFluxErr, \"\\\n", |
212 | 290 | " \"vis.expMidptMJD \"\\\n", |
213 | 291 | " \"FROM dp1.ForcedSourceOnDiaObject as fsodo \"\\\n", |
214 | 292 | " \"JOIN dp1.Visit as vis ON vis.visit = fsodo.visit \"\\\n", |
215 | | - " \"WHERE CONTAINS (POINT('ICRS', coord_ra, coord_dec), \"\\\n", |
216 | | - " \"CIRCLE('ICRS', \" + str(ra_var) + \", \"\\\n", |
217 | | - " + str(dec_var) + \", \" + str(search_rad) + \")) = 1 \"\n", |
| 293 | + " \"WHERE fsodo.diaObjectId = \"+str(DiaObjID)\n", |
218 | 294 | "print(query)" |
219 | 295 | ] |
220 | 296 | }, |
|
223 | 299 | "id": "600390ba-63e6-47b6-a91b-1c19a4f5ff87", |
224 | 300 | "metadata": {}, |
225 | 301 | "source": [ |
226 | | - "Run the TAP query, then extract the results of the TAP search to an Astropy table." |
| 302 | + "Run the TAP query." |
227 | 303 | ] |
228 | 304 | }, |
229 | 305 | { |
230 | 306 | "cell_type": "code", |
231 | 307 | "execution_count": null, |
232 | | - "id": "8685e9ba-5206-4ab9-9317-4f581ace2144", |
| 308 | + "id": "fca45813-9e8f-4571-ac51-ca10dac2046a", |
233 | 309 | "metadata": {}, |
234 | 310 | "outputs": [], |
235 | 311 | "source": [ |
|
238 | 314 | "job.wait(phases=['COMPLETED', 'ERROR'])\n", |
239 | 315 | "print('Job phase is', job.phase)\n", |
240 | 316 | "if job.phase == 'ERROR':\n", |
241 | | - " job.raise_if_error()\n", |
242 | | - "assert job.phase == 'COMPLETED'" |
| 317 | + " job.raise_if_error()" |
| 318 | + ] |
| 319 | + }, |
| 320 | + { |
| 321 | + "cell_type": "markdown", |
| 322 | + "id": "24628f61-7888-4df3-a72e-d2b18c6963c8", |
| 323 | + "metadata": {}, |
| 324 | + "source": [ |
| 325 | + "Fetch the results of the TAP search in table form and print the number of rows." |
243 | 326 | ] |
244 | 327 | }, |
245 | 328 | { |
246 | 329 | "cell_type": "code", |
247 | 330 | "execution_count": null, |
248 | | - "id": "b60aec9d-54ff-43fb-bacf-4d498b453887", |
| 331 | + "id": "8685e9ba-5206-4ab9-9317-4f581ace2144", |
249 | 332 | "metadata": {}, |
250 | 333 | "outputs": [], |
251 | 334 | "source": [ |
252 | | - "forced_sources = job.fetch_result().to_table()" |
| 335 | + "assert job.phase == 'COMPLETED'\n", |
| 336 | + "forced_source = job.fetch_result().to_table()\n", |
| 337 | + "print(len(forced_source))" |
253 | 338 | ] |
254 | 339 | }, |
255 | 340 | { |
|
267 | 352 | "metadata": {}, |
268 | 353 | "outputs": [], |
269 | 354 | "source": [ |
270 | | - "# forced_sources" |
| 355 | + "# forced_source" |
271 | 356 | ] |
272 | 357 | }, |
273 | 358 | { |
|
286 | 371 | "outputs": [], |
287 | 372 | "source": [ |
288 | 373 | "for band in filter_names:\n", |
289 | | - " print(f\"{band}: {np.sum((forced_sources['band'] == band))} visits\")" |
| 374 | + " print(f\"{band}: {np.sum((forced_source['band'] == band))} visits\")" |
290 | 375 | ] |
291 | 376 | }, |
292 | 377 | { |
|
312 | 397 | "fig.subplots_adjust(wspace=0, hspace=0)\n", |
313 | 398 | "\n", |
314 | 399 | "for band in filter_names:\n", |
315 | | - " pickband = np.where(forced_sources['band'] == band)\n", |
316 | | - " ax1.plot(forced_sources[pickband]['expMidptMJD'], forced_sources[pickband]['psfDiffFlux'],\n", |
| 400 | + " pickband = np.where(forced_source['band'] == band)\n", |
| 401 | + " ax1.plot(forced_source[pickband]['expMidptMJD'], forced_source[pickband]['psfDiffFlux'],\n", |
317 | 402 | " marker=filter_symbols[band], color=filter_colors[band],\n", |
318 | 403 | " label=f\"{band}\", markersize=7, linestyle='none', fillstyle='none')\n", |
319 | | - " ax2.plot(forced_sources[pickband]['expMidptMJD'], forced_sources[pickband]['psfFlux'],\n", |
| 404 | + " ax2.plot(forced_source[pickband]['expMidptMJD'], forced_source[pickband]['psfFlux'],\n", |
320 | 405 | " marker=filter_symbols[band], color=filter_colors[band], linestyle='none',\n", |
321 | 406 | " markersize=7, fillstyle='none')\n", |
322 | 407 | "\n", |
|
374 | 459 | "metadata": {}, |
375 | 460 | "outputs": [], |
376 | 461 | "source": [ |
377 | | - "pickr = np.where(forced_sources['band'] == 'r')\n", |
378 | | - "ind_max = np.argmax(forced_sources['psfFlux'][pickr])\n", |
379 | | - "t0 = forced_sources['expMidptMJD'][pickr][ind_max]\n", |
380 | | - "mjd_norm = (forced_sources['expMidptMJD'] - t0) / period\n", |
| 462 | + "pickr = np.where(forced_source['band'] == 'r')\n", |
| 463 | + "ind_max = np.argmax(forced_source['psfFlux'][pickr])\n", |
| 464 | + "t0 = forced_source['expMidptMJD'][pickr][ind_max]\n", |
| 465 | + "mjd_norm = (forced_source['expMidptMJD'] - t0) / period\n", |
381 | 466 | "phase = np.mod(mjd_norm, 1.0)" |
382 | 467 | ] |
383 | 468 | }, |
|
400 | 485 | "fig.subplots_adjust(wspace=0, hspace=0)\n", |
401 | 486 | "\n", |
402 | 487 | "for band in filter_names:\n", |
403 | | - " pickband = np.where((forced_sources['band'] == band)\n", |
404 | | - " & (forced_sources['psfDiffFlux'] > -50000))\n", |
405 | | - " ax1.plot(phase[pickband], forced_sources[pickband]['psfDiffFlux'],\n", |
| 488 | + " pickband = np.where((forced_source['band'] == band)\n", |
| 489 | + " & (forced_source['psfDiffFlux'] > -50000))\n", |
| 490 | + " ax1.plot(phase[pickband], forced_source[pickband]['psfDiffFlux'],\n", |
406 | 491 | " marker=filter_symbols[band], color=filter_colors[band],\n", |
407 | 492 | " label=f\"{band}\", markersize=7, linestyle='none', fillstyle='none')\n", |
408 | | - " ax2.plot(phase[pickband], forced_sources[pickband]['psfFlux'],\n", |
| 493 | + " ax2.plot(phase[pickband], forced_source[pickband]['psfFlux'],\n", |
409 | 494 | " marker=filter_symbols[band], color=filter_colors[band], linestyle='none',\n", |
410 | 495 | " markersize=7, fillstyle='none')\n", |
411 | 496 | "\n", |
|
443 | 528 | "metadata": {}, |
444 | 529 | "outputs": [], |
445 | 530 | "source": [ |
446 | | - "mag = forced_sources['psfFlux'].to(u.ABmag).value" |
| 531 | + "mag = forced_source['psfFlux'].to(u.ABmag).value" |
447 | 532 | ] |
448 | 533 | }, |
449 | 534 | { |
|
456 | 541 | "fig, ax = plt.subplots(1, 1, figsize=(7, 5))\n", |
457 | 542 | "\n", |
458 | 543 | "for band in filter_names:\n", |
459 | | - " pickband = np.where(forced_sources['band'] == band)\n", |
| 544 | + " pickband = np.where(forced_source['band'] == band)\n", |
460 | 545 | " ax.plot(phase[pickband], mag[pickband],\n", |
461 | 546 | " marker=filter_symbols[band], color=filter_colors[band],\n", |
462 | 547 | " label=f\"{band}\", markersize=7, linestyle='none', fillstyle='none')\n", |
|
0 commit comments