DA white dwarf variability#
In this tutorial, we will:
collect known DA white dwarf positions from recent literature, via SIMBAD bibcode queries and a VizieR table
load Rubin DP2 forced-source photometry with LSDB, including new columns with corrected flux-error estimates
cross-match the two catalogs and plot the resulting light curves, comparing original and corrected flux errors
Introduction#
Prerequisites#
In order to access Rubin data, you must be a Rubin data rights holder, and run this notebook from a platform with access to the DP2 HATS catalogs, such as the Rubin Science Platform (RSP).
[1]:
import os
import astropy.table
import lsdb
import matplotlib.pyplot as plt
import numpy as np
from astroquery.simbad import Simbad
from astroquery.vizier import Vizier
from dask.distributed import Client
plt.rcParams.update(
{
"axes.titlesize": 16,
"axes.labelsize": 15,
"xtick.labelsize": 13,
"ytick.labelsize": 13,
"legend.fontsize": 13,
"figure.titlesize": 18,
}
)
[2]:
import logging
logging.getLogger().setLevel(logging.WARNING)
logging.getLogger("distributed").setLevel(logging.WARNING)
[3]:
lsdb.show_versions()
-------- SYSTEM INFO --------
python : 3.13.9
python-bits : 64
OS : Linux
OS-release : 6.12.85+
Version : #1 SMP Mon May 11 07:53:24 UTC 2026
machine : x86_64
processor :
byteorder : little
LC_ALL :
LANG :
-------- INSTALLED VERSIONS --------
lsdb : 0.10.4
hats : 0.10.4
nested-pandas : 0.6.10
pandas : 2.3.3
numpy : 2.3.5
dask : 2026.7.1
pyarrow : 21.0.0
fsspec : 2026.6.0
1. Collect DA white dwarf positions from multiple papers#
We query SIMBAD for the objects cited in three recent DA white dwarf papers, identified by their bibcodes, and add one more table of positions from VizieR.
[4]:
simbad_bibcodes = [
"2025A&A...701A.269G",
"2025MNRAS.540..385B",
"2025AJ....169...40B",
]
vizier_catalogs = [
"J/A+A/699/A212/tableb2", # https://ui.adsabs.harvard.edu/abs/2025A%26A...699A.212J
]
simbad = Simbad()
vizier = Vizier(columns=["_RAJ2000", "_DEJ2000"])
Query each paper’s objects from SIMBAD and the VizieR table, keeping only right ascension and declination, and stack the results into a single astropy table.
[5]:
tables = []
for bibcode in simbad_bibcodes:
table = simbad.query_bibobj(bibcode)[["ra", "dec"]]
tables.append(table)
for viz_cat in vizier_catalogs:
table = vizier.get_catalogs(viz_cat)[0][["_RAJ2000", "_DEJ2000"]]
table.rename_columns(["_RAJ2000", "_DEJ2000"], ["ra", "dec"])
tables.append(table)
table = astropy.table.vstack(tables, metadata_conflicts="silent")
table
[5]:
| ra | dec |
|---|---|
| deg | deg |
| float64 | float64 |
| 24.0567308228 | -11.3423980207 |
| 349.9933320184 | -5.1656030952 |
| 102.2336908394 | -25.3963773716 |
| 90.0870523566 | -10.2345829685 |
| 67.3593234723 | -4.8126710103 |
| 77.5118826901 | -0.6321245159 |
| 165.3010036032 | -13.2450631543 |
| 154.9681774742 | -14.1261809958 |
| 158.4281790018 | -11.6939835104 |
| ... | ... |
| 215.8831569700 | 51.1985618600 |
| 220.1599867400 | 15.6816023800 |
| 224.2298896400 | 52.9683501500 |
| 236.7950789400 | 44.4800542400 |
| 239.7199891200 | 42.1809409700 |
| 244.4051143600 | 53.8164746700 |
| 247.1080496800 | 33.5806735700 |
| 261.1176597500 | 58.5941397000 |
| 269.8038006200 | 61.2159802400 |
1.1 Convert to an LSDB catalog#
We wrap the combined table with lsdb.from_astropy so it can be used as one side of the cross-match below.
[6]:
da_cat = lsdb.from_astropy(table)
da_cat
[6]:
| ra | dec | |
|---|---|---|
| npartitions=12 | ||
| Order: 0, Pixel: 0 | double[pyarrow] | double[pyarrow] |
| ... | ... | ... |
| Order: 0, Pixel: 10 | ... | ... |
| Order: 0, Pixel: 11 | ... | ... |
2. Load the Rubin DP2 forced-source catalog#
We open the DP2 object collection catalog, requesting all default columns plus two new nested forced-source subcolumns: psfFluxErr_corrected and psfDiffFluxErr_corrected.
Four new subcolumns are added to the nested forced-source tables (objectForcedSource and diaObjectForcedSource): psfFluxErr_corrected, psfDiffFluxErr_corrected, psfFluxErr_corrected_flag, and psfDiffFluxErr_corrected_flag. They come from a model trained to correct flux errors so that, for non-variable objects, the light curve’s reduced χ² is close to unity — these corrected values may be useful for light-curve fitting. The flag columns are True when the ratio between
the original and corrected errors falls outside the [0.1, 50] interval. See Malanchev et al., in prep., for details; source code is available on GitHub.
We also filter to objects with PSF magnitude brighter than 22 in every band, to keep the cross-match in this tutorial small and fast.
[7]:
dp2_obj = lsdb.open_catalog(
"/rubin/lsdb_data/object_collection",
columns=[
..., # all default columns
"objectForcedSource.psfFluxErr_corrected",
"objectForcedSource.psfDiffFluxErr_corrected",
],
# Filter data on the read time: keep objects which are brighter 22 mag
# in any band, since we know that these DA dwarfs are pretty bright.
filters=[[(f"{band}_psfMag", "<", 22) for band in "ugrizy"]],
)
dp2_obj
[7]:
| coord_dec | coord_decErr | coord_ra | coord_raErr | g_psfFlux | g_psfFluxErr | g_psfMag | g_psfMagErr | i_psfFlux | i_psfFluxErr | i_psfMag | i_psfMagErr | objectId | patch | r_psfFlux | r_psfFluxErr | r_psfMag | r_psfMagErr | refBand | shape_flag | shape_xx | shape_xy | shape_yy | tract | u_psfFlux | u_psfFluxErr | u_psfMag | u_psfMagErr | y_psfFlux | y_psfFluxErr | y_psfMag | y_psfMagErr | z_psfFlux | z_psfFluxErr | z_psfMag | z_psfMagErr | objectForcedSource | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| npartitions=8840 | |||||||||||||||||||||||||||||||||||||
| Order: 8, Pixel: 10240 | double[pyarrow] | float[pyarrow] | double[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | int64[pyarrow] | int64[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | string[pyarrow] | bool[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | int64[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | nested<band: [string], coord_dec: [double], co... |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| Order: 5, Pixel: 12283 | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| Order: 5, Pixel: 12284 | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
3. Cross-match with Rubin DP2#
We cross-match the literature white dwarf positions against the DP2 objects within 5 arcsec. As with all LSDB operations, this only plans the cross-match — no data has been loaded yet (see Lazy evaluation).
[8]:
xmatch = dp2_obj.crossmatch(da_cat, radius_arcsec=5, suffix_method="overlapping_columns")
xmatch
[8]:
| coord_dec | coord_decErr | coord_ra | coord_raErr | g_psfFlux | g_psfFluxErr | g_psfMag | g_psfMagErr | i_psfFlux | i_psfFluxErr | i_psfMag | i_psfMagErr | objectId | patch | r_psfFlux | r_psfFluxErr | r_psfMag | r_psfMagErr | refBand | shape_flag | shape_xx | shape_xy | shape_yy | tract | u_psfFlux | u_psfFluxErr | u_psfMag | u_psfMagErr | y_psfFlux | y_psfFluxErr | y_psfMag | y_psfMagErr | z_psfFlux | z_psfFluxErr | z_psfMag | z_psfMagErr | objectForcedSource | ra | dec | _dist_arcsec | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| npartitions=16 | ||||||||||||||||||||||||||||||||||||||||
| Order: 5, Pixel: 4505 | double[pyarrow] | float[pyarrow] | double[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | int64[pyarrow] | int64[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | string[pyarrow] | bool[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | int64[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | float[pyarrow] | nested<band: [string], coord_dec: [double], co... | double[pyarrow] | double[pyarrow] | double[pyarrow] |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| Order: 7, Pixel: 195352 | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| Order: 6, Pixel: 49101 | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
4. Run the pipeline#
Now we create a local Dask cluster and run the cross-match. See the Dask Client tutorial for tips on configuring Dask for LSDB.
[9]:
with Client(
n_workers=2,
threads_per_worker=1,
memory_limit="8GB",
local_directory=f"/deleted-sundays/{os.environ.get('USER', 'dask_scratch')}",
) as client:
print(f"Dask dashboard: {client.dashboard_link}")
display(client)
df = xmatch.compute()
df
/opt/lsst/software/stack/conda/envs/lsst-scipipe-12.3.0-exact/lib/python3.13/site-packages/distributed/node.py:188: UserWarning: Port 8787 is already in use.
Perhaps you already have a cluster running?
Hosting the HTTP server on port 43221 instead
warnings.warn(
Dask dashboard: https://ncaplar.nb.data-int.lsst.cloud/nb/user/ncaplar/proxy/43221/status
Client
Client-b2bbf605-89e7-11f1-88b3-49a1f880708d
| Connection method: Cluster object | Cluster type: distributed.LocalCluster |
| Dashboard: https://ncaplar.nb.data-int.lsst.cloud/nb/user/ncaplar/proxy/43221/status |
Cluster Info
LocalCluster
4958516b
| Dashboard: https://ncaplar.nb.data-int.lsst.cloud/nb/user/ncaplar/proxy/43221/status | Workers: 2 |
| Total threads: 2 | Total memory: 14.90 GiB |
| Status: running | Using processes: True |
Scheduler Info
Scheduler
Scheduler-391f057e-b8de-4d8a-9399-0e322dc3a8b7
| Comm: tcp://127.0.0.1:44899 | Workers: 0 |
| Dashboard: https://ncaplar.nb.data-int.lsst.cloud/nb/user/ncaplar/proxy/43221/status | Total threads: 0 |
| Started: Just now | Total memory: 0 B |
Workers
Worker: 0
| Comm: tcp://127.0.0.1:43823 | Total threads: 1 |
| Dashboard: https://ncaplar.nb.data-int.lsst.cloud/nb/user/ncaplar/proxy/37309/status | Memory: 7.45 GiB |
| Nanny: tcp://127.0.0.1:36317 | |
| Local directory: /deleted-sundays/ncaplar/dask-scratch-space/worker-7xzjr_i9 | |
Worker: 1
| Comm: tcp://127.0.0.1:40253 | Total threads: 1 |
| Dashboard: https://ncaplar.nb.data-int.lsst.cloud/nb/user/ncaplar/proxy/46577/status | Memory: 7.45 GiB |
| Nanny: tcp://127.0.0.1:34883 | |
| Local directory: /deleted-sundays/ncaplar/dask-scratch-space/worker-rrtih4e3 | |
[9]:
| coord_dec | coord_decErr | coord_ra | coord_raErr | g_psfFlux | g_psfFluxErr | g_psfMag | g_psfMagErr | i_psfFlux | i_psfFluxErr | i_psfMag | i_psfMagErr | objectId | patch | r_psfFlux | r_psfFluxErr | r_psfMag | r_psfMagErr | refBand | shape_flag | shape_xx | shape_xy | shape_yy | tract | u_psfFlux | u_psfFluxErr | u_psfMag | u_psfMagErr | y_psfFlux | y_psfFluxErr | y_psfMag | y_psfMagErr | z_psfFlux | z_psfFluxErr | z_psfMag | z_psfMagErr | objectForcedSource | ra | dec | _dist_arcsec | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1918058228703156167 | 2.339937 | 0.000000 | 151.161863 | 0.000000 | 61795.765625 | 27.427217 | 19.422604 | 0.000482 | 79364.007812 | 36.498375 | 19.150942 | 0.000499 | 788021220577669107 | 58 | 75718.742188 | 30.212614 | 19.201992 | 0.000433 | i | False | 4.252028 | 0.059288 | 3.969067 | 9814 | 39297.957031 | 54.880722 | 19.914076 | 0.001516 | 74448.414062 | 290.044373 | 19.220362 | 0.004230 | 77672.953125 | 71.986633 | 19.174326 | 0.001006 |
|
151.161887 | 2.339897 | 0.167428 | |||||||||||||||
| 1918186924080762372 | 2.642915 | 0.000000 | 150.653521 | 0.000000 | 188631.109375 | 43.119041 | 18.210966 | 0.000248 | 135714.796875 | 41.864441 | 18.568432 | 0.000335 | 788015310702668377 | 72 | 163191.390625 | 37.791920 | 18.368258 | 0.000251 | i | False | 4.255050 | -0.003524 | 3.928190 | 9813 | 140010.812500 | 85.813362 | 18.534595 | 0.000665 | 98306.085938 | 323.113373 | 18.918549 | 0.003569 | 113173.359375 | 78.492111 | 18.765640 | 0.000753 |
|
150.653515 | 2.642941 | 0.095025 | |||||||||||||||
| 2164146647781502823 | -15.598661 | 0.000001 | 244.479173 | 0.000001 | 111321.867188 | 329.714508 | 18.783548 | 0.003216 | 235259.250000 | 602.124512 | 17.971132 | 0.002779 | 768628378084333113 | 55 | 101022.460938 | 360.336945 | 18.888956 | 0.003873 | y | False | 8.765546 | 1.257625 | 8.170347 | 6992 | 420306.500000 | 1410.898804 | 17.341085 | 0.003645 | 112510.476562 | 578.089600 | 18.772018 | 0.005579 | 91457.890625 | 358.204437 | 18.996946 | 0.004252 |
|
244.480171 | -15.597821 | 4.596668 | |||||||||||||||
| 3377248907570746225 | -27.208014 | 0.000004 | 297.333079 | 0.000004 | 213423.062500 | 413.866516 | 18.076897 | 0.002105 | 137101.593750 | 468.876129 | 18.557394 | 0.003713 | 756383117085792759 | 63 | 270967.531250 | 442.270630 | 17.817707 | 0.001772 | y | False | 9.512555 | -0.473075 | 7.532055 | 5210 | 71865.968750 | 1241.068848 | 19.258692 | 0.018752 | 54183.125000 | 813.423279 | 19.565340 | 0.016301 | 15299.652344 | 547.012817 | 20.938295 | 0.038835 |
|
297.332407 | -27.207150 | 3.778421 | |||||||||||||||
| 3377248907581042017 | -27.208113 | 0.000005 | 297.332446 | 0.000007 | 175127.578125 | 539.417358 | 18.291615 | 0.003344 | 173459.453125 | 425.247406 | 18.302006 | 0.002662 | 756383117085792757 | 63 | 289461.656250 | 466.498749 | 17.746023 | 0.001750 | z | True | 18.028206 | -10.098429 | 18.860741 | 5210 | 58675.542969 | 1298.712891 | 19.478857 | 0.024035 | 95707.734375 | 850.153503 | 18.947632 | 0.009645 | 77914.796875 | 515.328979 | 19.170950 | 0.007181 |
|
297.332407 | -27.207150 | 3.466011 | |||||||||||||||
| 3377248907851940554 | -27.206832 | 0.000001 | 297.333607 | 0.000001 | 33922.066406 | 181.681366 | 20.073793 | 0.005815 | 135133.453125 | 190.545258 | 18.573093 | 0.001531 | 756383048366316850 | 62 | 88333.976562 | 200.714844 | 19.034681 | 0.002467 | z | False | 7.478521 | -0.459441 | 7.301531 | 5210 | 7160.368164 | 168.719543 | 21.762661 | 0.025588 | 194107.500000 | 755.787781 | 18.179893 | 0.004227 | 163486.062500 | 374.220642 | 18.366299 | 0.002485 |
|
297.332407 | -27.207150 | 4.009363 | |||||||||||||||
| 3436679681683128908 | -17.212113 | 0.000024 | 304.375029 | 0.000043 | 103467.875000 | 361.999237 | 18.862986 | 0.003799 | 42099.976562 | 176.454575 | 19.839294 | 0.004551 | 767287661093209606 | 45 | 55260.675781 | 278.631104 | 19.543959 | 0.005474 | y | False | 55.105633 | -11.382222 | 36.802410 | 6797 | 79772.148438 | 1019.541016 | 19.145372 | 0.013877 | 8311.605469 | 517.565247 | 21.600788 | 0.067697 | 28981.425781 | 426.063629 | 20.244701 | 0.015963 |
|
304.375005 | -17.211357 | 2.722975 | |||||||||||||||
| 3436679681692705429 | -17.212204 | 0.000000 | 304.374529 | 0.000000 | 182583.562500 | 412.527496 | 18.246346 | 0.002453 | 3123214.000000 | 723.848938 | 15.163496 | 0.000252 | 767287661093209601 | 45 | 282743.250000 | 375.685516 | 17.771519 | 0.001443 | y | False | 5.720750 | 0.027560 | 5.558167 | 6797 | 2379499.000000 | 2168.796143 | 15.458786 | 0.000990 | 2417262.250000 | 1087.127441 | 15.441690 | 0.000488 | 2765065.250000 | 1147.374634 | 15.295736 | 0.000450 |
|
304.375005 | -17.211357 | 3.457759 |
5. Plot the light curves#
For each matched white dwarf, we plot the per-band objectForcedSource light curves in both science flux and difference flux, comparing error bars from the original *Err columns against the corrected *Err_corrected columns, and report the reduced χ² for each.
[10]:
band_colors = {
"u": "#0c71ff",
"g": "#49be61",
"r": "#c61c00",
"i": "#ffc200",
"z": "#f341a2",
"y": "#5d0000",
}
bands = "ugrizy"
def reduced_chi2(y, yerr):
n = len(y)
if n <= 1:
return None
wmean = np.average(y, weights=1 / yerr**2)
chi2 = np.sum(((y - wmean) / yerr) ** 2)
return chi2 / (n - 1)
def chi2_text(value):
return "N/A" if value is None else f"{value:.2f}"
def plot_for_flux(
forced_sources,
ra,
dec,
plot_title,
flux_col,
err_col,
err_corr_col,
flag_col,
invalid_flag_col=None,
):
fig, axes = plt.subplots(2, 3, figsize=(15, 8), sharex=True)
axes = axes.ravel()
fig.suptitle(rf"$\alpha={ra:.5f}$, $\delta={dec:.5f}$" f"\n{plot_title}")
for ax, band in zip(axes, bands):
color = band_colors[band]
mask = (
(forced_sources["band"] == band)
& (~forced_sources[flag_col])
& (~forced_sources["pixelFlags_bad"])
& (~forced_sources["pixelFlags_saturated"])
& (~forced_sources["pixelFlags_nodata"])
)
if invalid_flag_col is not None and invalid_flag_col in forced_sources.columns:
mask &= ~forced_sources[invalid_flag_col]
lc = forced_sources[mask].copy()
chi2_orig = reduced_chi2(lc[flux_col], lc[err_col])
chi2_corr = reduced_chi2(lc[flux_col], lc[err_corr_col])
ax.errorbar(
lc["midpointMjdTai"],
lc[flux_col],
yerr=lc[err_corr_col],
color=color,
linestyle="none",
marker="o",
markersize=6,
markerfacecolor="none",
markeredgewidth=1.0,
alpha=0.35,
elinewidth=2.5,
capsize=3,
zorder=1,
label="corrected err",
)
ax.errorbar(
lc["midpointMjdTai"],
lc[flux_col],
yerr=lc[err_col],
color=color,
linestyle="none",
marker="x",
markersize=7,
alpha=0.95,
elinewidth=2.0,
capsize=2,
zorder=2,
label="orig err",
)
ax.set_title(
f"{band}-band {plot_title} "
f"$\\chi^2_\\nu$={chi2_text(chi2_orig)} "
f"$\\chi^2_\\nu$$_{{corr}}$={chi2_text(chi2_corr)}",
fontsize=12,
)
ax.grid(alpha=0.3)
ax.legend(loc="lower right", fontsize=11)
axes[0].set_ylabel(flux_col)
axes[3].set_ylabel(flux_col)
for ax in axes[3:]:
ax.set_xlabel("MJD")
fig.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
for row_index in range(len(df)):
row = df.iloc[row_index]
forced_sources = row["objectForcedSource"]
ra = row["coord_ra"]
dec = row["coord_dec"]
plot_configs = [
(
"science Flux",
"psfFlux",
"psfFluxErr",
"psfFluxErr_corrected",
"psfFlux_flag",
"invalidPsfFlag",
),
(
"differential Flux",
"psfDiffFlux",
"psfDiffFluxErr",
"psfDiffFluxErr_corrected",
"psfDiffFlux_flag",
None,
),
]
for plot_title, flux_col, err_col, err_corr_col, flag_col, invalid_flag_col in plot_configs:
plot_for_flux(
forced_sources=forced_sources,
ra=ra,
dec=dec,
plot_title=plot_title,
flux_col=flux_col,
err_col=err_col,
err_corr_col=err_corr_col,
flag_col=flag_col,
invalid_flag_col=invalid_flag_col,
)
Bright objects see their errors slightly overestimated, by about 30%, in exchange for reliably catching large error underestimation present in the original data.
About#
Authors: Konstantin Malanchev
Last run: July 27, 2026
If you use lsdb for published research, please cite following instructions.