@@ -1473,67 +1473,105 @@ def determineDistortion(self, adinputs=None, **params):
14731473 initial_peaks , _ = peak_finding .find_wavelet_peaks (
14741474 data , widths = widths , mask = mask & DQ .not_signal ,
14751475 variance = variance , min_snr = min_snr , reject_bad = debug_reject_bad )
1476- log .stdinfo (f"Found { len (initial_peaks )} peaks" )
14771476 # The coordinates are always returned as (x-coords, y-coords)
14781477 rwidth = 0.42466 * fwidth
14791478
1480- # Straight slits, such as in longslit, can have all the lines
1481- # traced simultaneously since they all have the same starting
1482- # point. "Curved" slits need to be handled one-by-one. This is
1483- # quite a bit slower, so this block of code does the line
1484- # tracing based on the slit involved.
1485- if constant_slit :
1486- traces = tracing .trace_lines (
1487- # Only need a single `start` value for all lines.
1488- ext , axis = 1 - dispaxis ,
1489- start = start , initial = initial_peaks ,
1490- rwidth = rwidth , cwidth = max (int (fwidth ), 5 ), step = step ,
1491- nsum = nsum , max_missed = max_missed ,
1492- max_shift = max_shift * ybin / xbin ,
1493- viewer = self .viewer if debug else None ,
1494- min_line_length = min_line_length )
1495-
1496- else :
1497- traces = []
1498- for peak in initial_peaks :
1499- # Need to start midway along the slit, which varies
1500- # along the dispersion axis. `extract_info` here is the
1501- # polynomial describing that midway line.
1502- start = extract_info (peak )
1503- traces .extend (tracing .trace_lines (
1479+ if len (initial_peaks ):
1480+ # The slit length may be smaller than the width of the slice,
1481+ # so we need to estimate the slit length and
1482+ # "min_line_length" is the fraction of that, not the fraction
1483+ # of the slice width.
1484+ if ext .mask is not None :
1485+ loc = int (np .median (initial_peaks ))
1486+ if dispaxis == 0 :
1487+ _slice = ext .mask [loc ] & DQ .unilluminated
1488+ else :
1489+ _slice = ext .mask [:, loc ] & DQ .unilluminated
1490+ slit_length_frac = 1 - ((_slice .argmin () +
1491+ _slice [::- 1 ].argmin ()) / _slice .size )
1492+ else :
1493+ try :
1494+ slit_length_frac = ad .MDF ['slitlength_pixels' ] / ext .shape [1 - dispaxis ]
1495+ except (AttributeError , KeyError ):
1496+ slit_length_frac = 1
1497+
1498+ # Straight slits, such as in longslit, can have all the lines
1499+ # traced simultaneously since they all have the same starting
1500+ # point. "Curved" slits need to be handled one-by-one. This is
1501+ # quite a bit slower, so this block of code does the line
1502+ # tracing based on the slit involved.
1503+ if constant_slit :
1504+ traces = tracing .trace_lines (
1505+ # Only need a single `start` value for all lines.
15041506 ext , axis = 1 - dispaxis ,
1505- start = start , initial = [ peak ] ,
1507+ start = start , initial = initial_peaks ,
15061508 rwidth = rwidth , cwidth = max (int (fwidth ), 5 ), step = step ,
15071509 nsum = nsum , max_missed = max_missed ,
15081510 max_shift = max_shift * ybin / xbin ,
15091511 viewer = self .viewer if debug else None ,
1510- min_line_length = 0.1 ) )
1512+ min_line_length = min_line_length * slit_length_frac )
15111513
1512- # List of traced peak positions
1513- in_coords = np .array ([coord for trace in traces for
1514- coord in trace .input_coordinates ()]).T
1514+ else :
1515+ traces = []
1516+ for peak in initial_peaks :
1517+ # Need to start midway along the slit, which varies
1518+ # along the dispersion axis. `extract_info` here is the
1519+ # polynomial describing that midway line.
1520+ start = extract_info (peak )
1521+ traces .extend (tracing .trace_lines (
1522+ ext , axis = 1 - dispaxis ,
1523+ start = start , initial = [peak ],
1524+ rwidth = rwidth , cwidth = max (int (fwidth ), 5 ), step = step ,
1525+ nsum = nsum , max_missed = max_missed ,
1526+ max_shift = max_shift * ybin / xbin ,
1527+ viewer = self .viewer if debug else None ,
1528+ min_line_length = min_line_length * slit_length_frac ))
1529+
1530+ log .stdinfo (f"Traced { len (traces )} lines from "
1531+ f"{ len (initial_peaks )} peaks" )
1532+
1533+ # List of traced peak positions
1534+ in_coords = np .array ([coord for trace in traces for
1535+ coord in trace .input_coordinates ()]).T
1536+
1537+ # We can't do anything if we have no coordinates
1538+ if in_coords .size == 0 :
1539+ log .warning ("Failed to trace any lines for "
1540+ f"{ ad .filename } :{ ext .id } " )
1541+ continue
15151542
1516- # We can't do anything if we have no coordinates
1517- if in_coords .size == 0 :
1518- log .warning ("Failed to trace any lines for "
1519- f"{ ad .filename } :{ ext .id } " )
1543+ else :
1544+ log .warning ("Failed to find any peaks in "
1545+ "f{ad.filename}:{ext.id}" )
15201546 continue
15211547
1522- # If there's a "rectified" frame, we want to use the pixel
1523- # coordinates in *that* frame as input so that the pixels
1524- # -> rectified -> distortion_corrected transform works
1525- # correctly.
1526- try :
1527- t = ext .wcs .get_transform (ext .wcs .input_frame , 'rectified' )
1528- except CoordinateFrameError :
1529- pass
1530- else :
1531- in_coords = np .array (t (* in_coords ))
15321548 # List of "reference" positions (i.e., the coordinate
15331549 # perpendicular to the line remains constant at its initial value
15341550 ref_coords = np .array ([coord for trace in traces for
15351551 coord in trace .reference_coordinates ()]).T
15361552
1553+ # If the frame has a rectification model, then we want to
1554+ # calculate the distortion transform *after* applying this
1555+ # model. This is important because, if we only have one line
1556+ # from which to determine the distortion, the model will only
1557+ # be a function of X and so we need a vertical spectrum.
1558+ try :
1559+ rect_model = ext .wcs .get_transform (ext .wcs .input_frame ,
1560+ "rectified" )
1561+ except CoordinateFrameError :
1562+ has_rect_model = False
1563+ else :
1564+ has_rect_model = True
1565+ in_coords = rect_model (* in_coords )
1566+ # For a vertically-dispersed spectrum, the rectification
1567+ # model alters X as a function of Y. Therefore, because
1568+ # incoords and ref_coords have different Y for the same X,
1569+ # putting them *both* through the rectification model will
1570+ # produce different X values, which is not what we want.
1571+ # S0 replace the X values in ref_coords with the ones in
1572+ # in_coords. Coords are *always* (x, y)
1573+ ref_coords [dispaxis ] = in_coords [dispaxis ]
1574+
15371575 # The model is computed entirely in the pixel coordinate frame
15381576 # of the data, so it could be used as a gWCS object
15391577 m_init = models .Chebyshev2D (x_degree = orders [1 - dispaxis ],
@@ -1573,8 +1611,25 @@ def determineDistortion(self, adinputs=None, **params):
15731611 ext .wcs = gWCS ([(cf .Frame2D (name = "pixels" ), model ),
15741612 (cf .Frame2D (name = "world" ), None )])
15751613 else :
1576- ext .wcs .insert_frame (ext .wcs .input_frame , model ,
1577- cf .Frame2D (name = "distortion_corrected" ))
1614+ try :
1615+ frame_index = ext .wcs .available_frames .index ("distortion_corrected" )
1616+ except ValueError :
1617+ pass
1618+ else :
1619+ log .warning ("Deleting existing distortion model in "
1620+ f"{ ad .filename } :{ ext .id } " )
1621+ ext .wcs = ext .wcs .__class__ (
1622+ ext .wcs .pipeline [:frame_index - 1 ] +
1623+ [(ext .wcs .pipeline [frame_index - 1 ].frame ,
1624+ ext .wcs .pipeline [frame_index ].transform )] +
1625+ ext .wcs .pipeline [frame_index + 1 :]
1626+ )
1627+ if has_rect_model :
1628+ ext .wcs .insert_frame ("rectified" , model ,
1629+ cf .Frame2D (name = "distortion_corrected" ))
1630+ else :
1631+ ext .wcs .insert_frame (ext .wcs .input_frame , model ,
1632+ cf .Frame2D (name = "distortion_corrected" ))
15781633
15791634 nsuccess += 1
15801635
0 commit comments