Repository: lightkurve/lightkurve

Issue:
`SFFCorrector` appears to crash when the aperture mask contains only one pixel 
Example:
```python
In [1]: import lightkurve as lk                                                                                               

In [2]: tpf = lk.search_targetpixelfile("EPIC 211771334", campaign=18).download()                                             

In [3]: tpf.to_lightcurve(aperture_mask="threshold").to_corrector("sff").correct()                                            
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:610: RuntimeWarning: divide by zero encountered in true_divide
  slope = (y_hi - y_lo) / (x_hi - x_lo)[:, None]
/home/gb/bin/anaconda/lib/python3.7/site-packages/scipy/interpolate/interpolate.py:613: RuntimeWarning: invalid value encountered in multiply
  y_new = slope*(x_new - x_lo)[:, None] + y_lo
WARNING: Input data contains invalid values (NaNs or infs), which were automatically clipped. [astropy.stats.sigma_clipping]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-3-30cf4b808ec9> in <module>
----> 1 tpf.to_lightcurve(aperture_mask="threshold").to_corrector("sff").correct()

~/dev/lightkurve/lightkurve/correctors/sffcorrector.py in correct(self, centroid_col, centroid_row, polyorder, niters, bins, windows, sigma_1, sigma_2, restore_trend)
    117             tempflux = np.asarray([item for sublist in flux for item in sublist])
    118             flux_outliers = sigma_clip(data=tempflux, sigma=sigma_1).mask
--> 119             self.bspline = self.fit_bspline(new_lc.time[~flux_outliers], tempflux[~flux_outliers])
    120 
    121             # The SFF algorithm is going to be run on each window independently

~/dev/lightkurve/lightkurve/correctors/sffcorrector.py in fit_bspline(self, time, flux, knotspacing)
    230         """Returns a `scipy.interpolate.BSpline` object to interpolate flux as a function of time."""
    231         # By default, bspline knots are placed 1.5 days apart
--> 232         knots = np.arange(time[0], time[-1], knotspacing)
    233 
    234         # If the light curve has breaks larger than the spacing between knots,

IndexError: index 0 is out of bounds for axis 0 with size 0
```

Discussion:

Patch:
diff --git a/src/lightkurve/correctors/sffcorrector.py b/src/lightkurve/correctors/sffcorrector.py
index b92be92f..89c6b538 100644
--- a/src/lightkurve/correctors/sffcorrector.py
+++ b/src/lightkurve/correctors/sffcorrector.py
@@ -502,7 +502,12 @@ def _estimate_arclength(centroid_col, centroid_row):
     """
     col = centroid_col - np.nanmin(centroid_col)
     row = centroid_row - np.nanmin(centroid_row)
+    if np.all((col == 0) & (row == 0)):
+        raise RuntimeError("Arclength cannot be computed because there is no "
+                           "centroid motion. Make sure that the aperture of "
+                           "the TPF at least two pixels.")
     # Force c to be correlated not anticorrelated
     if np.polyfit(col.data, row.data, 1)[0] < 0:
         col = np.nanmax(col) - col
-    return (col ** 2 + row ** 2) ** 0.5
+    arclength = (col ** 2 + row ** 2) ** 0.5
+    return arclength 
diff --git a/src/lightkurve/io/generic.py b/src/lightkurve/io/generic.py
index f571d0e2..d051df68 100644
--- a/src/lightkurve/io/generic.py
+++ b/src/lightkurve/io/generic.py
@@ -67,7 +67,13 @@ def read_generic_lightcurve(
         elif unitstr == "ppm" and repr(tab[colname].unit).startswith("Unrecognized"):
             # Workaround for issue #956
             tab[colname].unit = ppm
-
+        elif unitstr == "ADU":
+            tab[colname].unit = "adu"
+        elif unitstr.lower() == "unitless":
+            tab[colname].unit = ""
+        elif unitstr.lower() == "degcelcius":
+            # CDIPS has non-astropy units
+            tab[colname].unit = "deg_C"
         # Rename columns to lowercase
         tab.rename_column(colname, colname.lower())
 


Test patch:
diff --git a/tests/io/test_cdips.py b/tests/io/test_cdips.py
index c67bff6d..b9f0ea7e 100644
--- a/tests/io/test_cdips.py
+++ b/tests/io/test_cdips.py
@@ -4,7 +4,7 @@ from astropy.io import fits
 import numpy as np
 from numpy.testing import assert_array_equal
 
-from lightkurve import search_lightcurve
+from lightkurve import search_lightcurve, LightCurveCollection
 from lightkurve.io.cdips import read_cdips_lightcurve
 from lightkurve.io.detect import detect_filetype
 
@@ -59,3 +59,7 @@ def test_search_cdips():
     lc = search.download()
     assert type(lc).__name__ == "TessLightCurve"
     assert hasattr(lc, "sector")
+    assert str(lc['bge'].unit) == 'adu'
+    slc = LightCurveCollection([lc, lc]).stitch()
+    assert len(slc) == 2*len(lc)
+