detect_order_edges

The detect_order_edges utility uses a fully-illuminated slit flat-lamp frame to detect and fit the edges of each echelle order across the detector plane.

Fig. 65 This algorithm detects and fits the edges of the echelle orders across the detector plane.

The utility takes as input a fully illuminated slit image (flat-field frame). The second input is the order table generated by the soxs_order_centres recipe, which provides a trace of the centre of each order along the dispersion axis.

An array of central pixel positions is generated using the order table for each order. At each pixel position in these arrays, an N-pixel long (slice-length-for-edge-detection), M-pixel wide (slice-width-for-edge-detection) image slice in the cross-dispersion direction is cut from the flat-frame. Each 2D slice is collapsed to a 1D array by taking the median value across its width (ignoring masked pixels).

The minimum and maximum fluxes are calculated from the central 1D slice in each order to give a flux range. The absolute minimum and maximum flux thresholds are determined from the percentage thresholds given as recipe parameters (min-percentage-threshold-for-edge-detection and max-percentage-threshold-for-edge-detection). If the minimum threshold were set at 25%, then the absolute flux would be:

\[ threshold = minvalue + (maxvalue - minvalue) * 0.25 \]

If the minimum flux is not recorded at any point along the order edge, the threshold slowly increments to the maximum flux until the threshold is registered. Slices where both edges are undetected before the maximum flux threshold is reached are rejected.

For each slice along each order, we measure the flux at the centre of the order and record a minimum and maximum percentage of this central flux (minimum and maximum percentages are set in the settings file). Then, we smooth the flux with a rolling-median filter for each cross-dispersion slice. Finally, we start at the minimum flux threshold to try and find both order edges simultaneously. If an edge can’t be found at this threshold, we incrementally increase the threshold until we find the edges or reach the maximum threshold. If found, the pixel-locations xmin (lower edge) and xmax (upper edge) where the flux reaches this minimum flux threshold are recorded (see red circles in Fig. 66). If, however, the maximum threshold is reached, then the slice will be discarded.

image-20240911115628697

Fig. 66 A single slice cut across the cross-dispersion axis of an echelle order. The grey dots represent the median flux from the width-collapsed 1D slice. The red dots show where a pre-determined flux threshold is reached, and the pixel positions at these locations are registered as the lower and upper edges of the order as measured in this slice.

For each order, a median order height (\(xmax-xmin\)) is determined, and \(xmin\) and \(xmax\) are adjusted for each slice, so each order now has the same pixel height across the order. This helps fit the order edges at the extremes of the detector plane where flux from the flat lamp can be reduced.

Finally, for each order, the arrays of \(xmin\) and \(xmax\) pixel-positions are iteratively fitted with low order polynomials:

\[ X = \sum\limits_{ij} c_{ij} \times n^i \times Y^j \]

where \(X\) and \(Y\) are the pixel positions and \(n\) is the echelle order number. \(i\) and \(j\) are the polynomial degree orders for the echelle order (order-deg) and \(Y\) pixel position (disp-axis-deg) respectively. \(c_{ij}\) are the polynomial coefficients to be fitted. The polynomial is iteratively fitted while sigma-clipping pixel-positions with outlying residuals (see Fig. 67). The new order location table is written to file and now includes the upper and lower-edge locations alongside the central location for each order.

image-20240911121836550

Fig. 67 The top panel shows the upper and lower-order edge detections registered in the individual cross-dispersion slices in an Xshooter VIS flat frame. The bottom panel shows the global polynomial fits to the upper and lower-order edges, with the area between the fits filled with different colours to reveal the unique echelle orders across the detector plane.

Utility API

class detect_order_edges(log, flatFrame, orderCentreTable, settings=False, recipeSettings=False, recipeName='soxs-mflat', verbose=False, qcTable=False, productsTable=False, tag='', sofName=False, binx=1, biny=1, extendToEdges=True, lampTag=False, startNightDate='', debug=False)[source]

Bases: soxspipe.commonutils._base_detect

using a fully-illuminated slit flat frame detect and record the order-edges

Key Arguments:

  • log – logger

  • settings – the settings dictionary

  • recipeSettings – the recipe specific settings

  • flatFrame – the flat frame to detect the order edges on

  • orderCentreTable – the order centre table

  • recipeName – name of the recipe as it appears in the settings dictionary

  • verbose – verbose. True or False. Default False

  • qcTable – the data frame to collect measured QC metrics

  • productsTable – the data frame to collect output products

  • tag – e.g. ‘_DLAMP’ to differentiate between UV-VIS lamps

  • sofName – name of the originating SOF file

  • binx – binning in x-axis

  • biny – binning in y-axis

  • extendToEdges – if true, extend the order edge tracing to the edges of the frame (Default True)

  • lampTag – add this tag to the end of the product filename (Default False)

  • startNightDate – YYYY-MM-DD date of the observation night. Default “”

Usage:

from soxspipe.commonutils import detect_order_edges
edges = detect_order_edges(
    log=log,
    flatFrame=flatFrame,
    orderCentreTable=orderCentreTable,
    settings=settings,
    recipeSettings=recipeSettings,
    recipeName="soxs-mflat",
    verbose=False,
    qcTable=False,
    productsTable=False,
    extendToEdges=True,
    lampTag=False
)
productsTable, qcTable, orderDetectionCounts = edges.get()

Initialization

calculate_residuals(orderPixelTable, coeff, axisACol, axisBCol, orderCol=False, writeQCs=False)[source]
determine_lower_upper_edge_pixel_positions(orderData)[source]

from a pixel postion somewhere on the trace of the order centre, return the lower and upper edges of the order

Key Arguments:

  • orderData – one row in the orderTable

Return:

  • orderData – orderData with upper and lower edge xcoord arrays added

determine_order_flux_threshold(orderData, orderPixelTable)[source]

determine the flux threshold at the central column of each order

Key Arguments:

  • orderData – one row in the orderTable

  • orderPixelTable the order table containing pixel arrays

Return:

  • orderData – orderData with min and max flux thresholds added

fit_global_polynomial(pixelList, axisACol='cont_x', axisBCol='cont_y', orderCol='order', exponentsIncluded=False, writeQCs=False)[source]
fit_order_polynomial(pixelList, order, axisBDeg, axisACol, axisBCol, exponentsIncluded=False)[source]
get()[source]

get the detect_order_edges object

Return:

  • orderTablePath – path to the new order table

plot_results(orderPixelTableUpper, orderPixelTableLower, orderPolyTable, orderMetaTable, clippedDataUpper, clippedDataLower)[source]

generate a plot of the polynomial fits and residuals

Key Arguments:

  • orderPixelTableUpper – the pixel table with residuals of fits for the upper edges

  • orderPixelTableLower – the pixel table with residuals of fits for the lower edges

  • orderPolyTable – data-frame of order-location polynomial coeff

  • orderMetaTable – data-frame containing the limits of the fit

  • clippedDataUpper – the sigma-clipped data from upper edge

  • clippedDataLower – the sigma-clipped data from lower edge

Return:

  • filePath – path to the plot pdf

write_order_table_to_file(frame, orderPolyTable, orderMetaTable)[source]