diff --git a/doc/releases/1.16.1dev.rst b/doc/releases/1.16.1dev.rst index 3b1e4ac604..4deebe8859 100644 --- a/doc/releases/1.16.1dev.rst +++ b/doc/releases/1.16.1dev.rst @@ -36,6 +36,20 @@ Instrument-specific Updates - Improved LRIS frame typing, including the typing of slitless flats and sky flats. - Improved HIRES frame typing and configuration setup. +- Updated X-Shooter detector gain and read noise to come from header, and + updated plate scales to the most recent values from the manual. Detailed + changes are: + - NIR arm: + - Platescale updated from 0.197 to 0.245 arcsec/pixel + - Dark current updated from 0. to 72. e-/pixel/hr + - Gain updated from 2.12 to 2.29 e-/DN + - VIS arm: + - Platescale updated from an order-dependent value, to being 0.154 + arcsec/pixel for all orders + - UVB arm: + - Platescale updated from an order-dependent value, to being 0.164 + arcsec/pixel for all orders + Script Changes -------------- @@ -57,6 +71,8 @@ Script Changes function (before only optimal extraction was used). - Added ``pypeit_show_pixflat`` script to inspect the (slitless) pixel flat generated during the reduction and stored in ``data/pixelflats``. +- Added ``pypeit_chk_flexure`` script to check both spatial and spectral flexure applied to + the reduced data. Datamodel Changes ----------------- diff --git a/pypeit/core/findobj_skymask.py b/pypeit/core/findobj_skymask.py index e2d624a37f..f919f70bda 100644 --- a/pypeit/core/findobj_skymask.py +++ b/pypeit/core/findobj_skymask.py @@ -1278,6 +1278,7 @@ def ech_objfind(image, ivar, slitmask, slit_left, slit_righ, slit_spat_id, order det=det, inmask=inmask, std_trace=std_trace, + ncoeff=ncoeff, specobj_dict=specobj_dict, snr_thresh=snr_thresh, show_peaks=show_peaks, diff --git a/pypeit/core/flexure.py b/pypeit/core/flexure.py index 02265a3426..a36e581ec4 100644 --- a/pypeit/core/flexure.py +++ b/pypeit/core/flexure.py @@ -17,6 +17,7 @@ from astropy import stats from astropy import units from astropy.io import ascii +from astropy.table import Table import scipy.signal import scipy.optimize as opt from scipy import interpolate @@ -36,7 +37,7 @@ from pypeit.datamodel import DataContainer from pypeit.images.detector_container import DetectorContainer from pypeit.images.mosaic import Mosaic -from pypeit import specobj, specobjs +from pypeit import specobj, specobjs, spec2dobj from pypeit import wavemodel from IPython import embed @@ -1394,6 +1395,80 @@ def sky_em_residuals(wave:np.ndarray, flux:np.ndarray, return dwave[m], diff[m], diff_err[m], los[m], los_err[m] +def flexure_diagnostic(file, file_type='spec2d', flexure_type='spec', chk_version=False): + """ + Print the spectral or spatial flexure of a spec2d or spec1d file + Args: + file (:obj:`str`, `Path`_): + Filename of the spec2d or spec1d file to check + file_type (:obj:`str`, optional): + Type of the file to check. Options are 'spec2d' or 'spec1d'. Default is 'spec2d'. + flexure_type (:obj:`str`, optional): + Type of flexure to check. Options are 'spec' or 'spat'. Default is 'spec'. + chk_version (:obj:`bool`, optional): + If True, raise an error if the datamodel version or type check failed. + If False, throw a warning only. Default is False. + + Returns: + :obj:`astropy.table.Table` or :obj:`float`: + - If file_type is 'spec2d' and flexure_type is 'spec', return a table with the spectral flexure + - If file_type is 'spec2d' and flexure_type is 'spat', return the spatial flexure + - If file_type is 'spec1d', return a table with the spectral flexure + + """ + + # value to return + return_flex = None + + if file_type == 'spec2d': + # load the spec2d file + allspec2D = spec2dobj.AllSpec2DObj.from_fits(file, chk_version=chk_version) + # Loop on Detectors + for det in allspec2D.detectors: + print('') + print('=' * 50 + f'{det:^7}' + '=' * 51) + # get and print the spectral flexure + if flexure_type == 'spec': + spec_flex = allspec2D[det].sci_spec_flexure + spec_flex.rename_column('sci_spec_flexure', 'global_spec_shift') + if np.all(spec_flex['global_spec_shift'] != None): + spec_flex['global_spec_shift'].format = '0.3f' + # print the table + spec_flex.pprint_all() + # return the table + return_flex = spec_flex + # get and print the spatial flexure + if flexure_type == 'spat': + spat_flex = allspec2D[det].sci_spat_flexure + # print the value + print(f'Spat shift: {spat_flex}') + # return the value + return_flex = spat_flex + elif file_type == 'spec1d': + # no spat flexure in spec1d file + if flexure_type == 'spat': + msgs.error("Spat flexure not available in the spec1d file, try with a spec2d file") + # load the spec1d file + sobjs = specobjs.SpecObjs.from_fitsfile(file, chk_version=chk_version) + spec_flex = Table() + spec_flex['NAME'] = sobjs.NAME + spec_flex['global_spec_shift'] = sobjs.FLEX_SHIFT_GLOBAL + if np.all(spec_flex['global_spec_shift'] != None): + spec_flex['global_spec_shift'].format = '0.3f' + spec_flex['local_spec_shift'] = sobjs.FLEX_SHIFT_LOCAL + if np.all(spec_flex['local_spec_shift'] != None): + spec_flex['local_spec_shift'].format = '0.3f' + spec_flex['total_spec_shift'] = sobjs.FLEX_SHIFT_TOTAL + if np.all(spec_flex['total_spec_shift'] != None): + spec_flex['total_spec_shift'].format = '0.3f' + # print the table + spec_flex.pprint_all() + # return the table + return_flex = spec_flex + + return return_flex + + # TODO -- Consider separating the methods from the DataContainer as per calibrations class MultiSlitFlexure(DataContainer): """ diff --git a/pypeit/display/display.py b/pypeit/display/display.py index 948ca1f2f8..9cb087bf59 100644 --- a/pypeit/display/display.py +++ b/pypeit/display/display.py @@ -60,7 +60,7 @@ def connect_to_ginga(host='localhost', port=9000, raise_err=False, allow_new=Fal # was just instantiated for a maximum number of iterations. # If the connection is remains unsuccessful, an error is # thrown stating that the connection timed out. - maxiter = int(1e6) + maxiter = int(3e4) for i in range(maxiter): try: viewer = grc.RemoteClient(host, port) @@ -72,8 +72,9 @@ def connect_to_ginga(host='localhost', port=9000, raise_err=False, allow_new=Fal break if i == maxiter-1: msgs.error('Timeout waiting for ginga to start. If window does not appear, type ' - '`ginga --modules=RC,SlitWavelength` on the command line. In either case, wait for ' - 'the ginga viewer to open and try the pypeit command again.') + '`ginga --modules=RC,SlitWavelength` on the command line. In either ' + 'case, wait for the ginga viewer to open and try the pypeit command ' + 'again.') return viewer if raise_err: diff --git a/pypeit/scripts/__init__.py b/pypeit/scripts/__init__.py index 4c8c160a91..9f4bef02c6 100644 --- a/pypeit/scripts/__init__.py +++ b/pypeit/scripts/__init__.py @@ -9,6 +9,7 @@ from pypeit.scripts import chk_alignments from pypeit.scripts import chk_edges from pypeit.scripts import chk_flats +from pypeit.scripts import chk_flexure from pypeit.scripts import chk_tilts from pypeit.scripts import chk_for_calibs from pypeit.scripts import chk_noise_1dspec diff --git a/pypeit/scripts/chk_flexure.py b/pypeit/scripts/chk_flexure.py new file mode 100644 index 0000000000..d384dc8e29 --- /dev/null +++ b/pypeit/scripts/chk_flexure.py @@ -0,0 +1,63 @@ +""" +This script displays the flexure (spatial or spectral) applied to the science data. + +.. include common links, assuming primary doc root is up one directory +.. include:: ../include/links.rst +""" + +from pypeit.scripts import scriptbase + + +class ChkFlexure(scriptbase.ScriptBase): + + @classmethod + def get_parser(cls, width=None): + parser = super().get_parser(description='Print QA on flexure to the screen', + width=width) + + parser.add_argument('input_file', type=str, nargs='+', help='One or more PypeIt spec2d or spec1d file') + inp = parser.add_mutually_exclusive_group(required=True) + inp.add_argument('--spec', default=False, action='store_true', help='Check the spectral flexure') + inp.add_argument('--spat', default=False, action='store_true', help='Check the spatial flexure') + parser.add_argument('--try_old', default=False, action='store_true', + help='Attempt to load old datamodel versions. A crash may ensue..') + return parser + + @staticmethod + def main(args): + + from IPython import embed + from astropy.io import fits + from pypeit import msgs + from pypeit.core import flexure + + chk_version = not args.try_old + + # Loop over the input files + for in_file in args.input_file: + + msgs.info(f'Checking fluxure for file: {in_file}') + + # What kind of file are we?? + hdul = fits.open(in_file) + head0 = hdul[0].header + file_type = None + if 'PYP_CLS' in head0.keys() and head0['PYP_CLS'].strip() == 'AllSpec2DObj': + file_type = 'spec2d' + elif 'DMODCLS' in head0.keys() and head0['DMODCLS'].strip() == 'SpecObjs': + file_type = 'spec1d' + else: + msgs.error("Bad file type input!") + + # Check the flexure + flexure.flexure_diagnostic(in_file, file_type=file_type, flexure_type='spat' if args.spat else 'spec', + chk_version=chk_version) + + # space between files for clarity + print('') + + + + + + diff --git a/pypeit/spectrographs/vlt_xshooter.py b/pypeit/spectrographs/vlt_xshooter.py index b12ac92cbb..98b5cf6895 100644 --- a/pypeit/spectrographs/vlt_xshooter.py +++ b/pypeit/spectrographs/vlt_xshooter.py @@ -222,6 +222,7 @@ def get_detector_par(self, det, hdu=None): :class:`~pypeit.images.detector_container.DetectorContainer`: Object with the detector metadata. """ + # Detector 1 detector_dict = dict( binning = '1,1', # No binning in near-IR @@ -230,21 +231,15 @@ def get_detector_par(self, det, hdu=None): specaxis = 1, specflip = False, spatflip = False, - platescale = 0.197, # average between order 11 & 30, see manual - darkcurr = 0.0, # e-/pixel/hour + platescale = 0.245, # average across all orders, see manual + darkcurr = 72.0, # e-/pixel/hour saturation = 2.0e5, # I think saturation may never be a problem here since there are many DITs nonlinear = 0.86, mincounts = -1e10, numamplifiers = 1, - gain = np.atleast_1d(2.12), # - ronoise = np.atleast_1d(8.0), # ?? more precise value? #TODO the read noise is exposure time dependent and should be grabbed from header + gain = np.atleast_1d(2.29), + ronoise = np.atleast_1d(8.0), # Read noise depends on exposure time (see XShooter manual) but this is the typical value. datasec = np.atleast_1d('[4:2044,4:]'), # These are all unbinned pixels - # EMA: No real overscan for XSHOOTER-NIR: - # See Table 6 in http://www.eso.org/sci/facilities/paranal/instruments/xshooter/doc/VLT-MAN-ESO-14650-4942_P103v1.pdf - # The overscan region below contains only zeros - # ToDo should we just set it as empty? - # JXP says yes - #oscansec = np.atleast_1d('[4:2044,1:3]'), # These are all unbinned pixels. ) return detector_container.DetectorContainer(**detector_dict) @@ -263,19 +258,12 @@ def default_pypeit_par(cls): turn_off = dict(use_illumflat=False, use_biasimage=False, use_overscan=False, use_darkimage=False) par.reset_all_processimages_par(**turn_off) - # Require dark images to be subtracted from the flat images used for - # tracing, pixelflats, and illumflats - # par['calibrations']['traceframe']['process']['use_darkimage'] = True - # par['calibrations']['pixelflatframe']['process']['use_darkimage'] = True - # par['calibrations']['illumflatframe']['process']['use_darkimage'] = True - # TODO: `mask_cr` now defaults to True for darks. Should this be turned off? # Is this needed below? par['scienceframe']['process']['sigclip'] = 20.0 par['scienceframe']['process']['satpix'] = 'nothing' # TODO tune up LA COSMICS parameters here for X-shooter as tellurics are being excessively masked - # Adjustments to slit and tilts for NIR par['calibrations']['slitedges']['edge_thresh'] = 50. par['calibrations']['slitedges']['fit_order'] = 8 @@ -304,16 +292,17 @@ def default_pypeit_par(cls): par['calibrations']['wavelengths']['reid_arxiv'] = 'vlt_xshooter_nir.fits' par['calibrations']['wavelengths']['cc_thresh'] = 0.50 par['calibrations']['wavelengths']['cc_local_thresh'] = 0.50 -# par['calibrations']['wavelengths']['ech_fix_format'] = True # Echelle parameters par['calibrations']['wavelengths']['echelle'] = True par['calibrations']['wavelengths']['ech_nspec_coeff'] = 5 par['calibrations']['wavelengths']['ech_norder_coeff'] = 5 par['calibrations']['wavelengths']['ech_sigrej'] = 3.0 par['calibrations']['wavelengths']['qa_log'] = False + # Measured FWHM is correct, but resulting wavelength solution is poor. + # This should be explored further, but for now, turning off fwhm_fromlines helps. + par['calibrations']['wavelengths']['fwhm_fromlines'] = False # Flats - #par['calibrations']['standardframe']['process']['illumflatten'] = False par['calibrations']['flatfield']['tweak_slits_thresh'] = 0.90 par['calibrations']['flatfield']['tweak_slits_maxfrac'] = 0.10 @@ -324,23 +313,10 @@ def default_pypeit_par(cls): par['reduce']['skysub']['bspline_spacing'] = 0.8 par['reduce']['skysub']['global_sky_std'] = False # Do not perform global sky subtraction for standard stars par['reduce']['extraction']['model_full_slit'] = True # local sky subtraction operates on entire slit - par['reduce']['findobj']['trace_npoly'] = 8 + par['reduce']['findobj']['trace_npoly'] = 10 par['reduce']['findobj']['maxnumber_sci'] = 2 # Assume that there is only one object on the slit. par['reduce']['findobj']['maxnumber_std'] = 1 # Assume that there is only one object on the slit. - - # The settings below enable X-shooter dark subtraction from the traceframe and pixelflatframe, but enforce - # that this bias won't be subtracted from other images. It is a hack for now, because eventually we want to - # perform this operation with the dark frame class, and we want to attach individual sets of darks to specific - # images. - #par['calibrations']['biasframe']['useframe'] = 'bias' - #par['calibrations']['traceframe']['process']['bias'] = 'force' - #par['calibrations']['pixelflatframe']['process']['bias'] = 'force' - #par['calibrations']['arcframe']['process']['bias'] = 'skip' - #par['calibrations']['tiltframe']['process']['bias'] = 'skip' - #par['calibrations']['standardframe']['process']['bias'] = 'skip' - #par['scienceframe']['process']['bias'] = 'skip' - # Sensitivity function parameters par['sensfunc']['algorithm'] = 'IR' par['sensfunc']['polyorder'] = 8 @@ -437,7 +413,7 @@ def check_frame_type(self, ftype, fitstbl, exprng=None): | (fitstbl['target'] == 'LAMP,FLAT')) & good_flat_seq) - if ftype in ['dark']: + if ftype in ['lampoffflats']: # Lamp off flats are taken second (even exposure number) return good_exp & (((fitstbl['target'] == 'LAMP,DFLAT') | (fitstbl['target'] == 'LAMP,QFLAT') @@ -488,32 +464,6 @@ def bpm(self, filename, det, shape=None, msbias=None): vlt_sc = dataPaths.static_calibs / 'vlt_xshoooter' bpm_loc = np.loadtxt(vlt_sc.get_file_path('BP_MAP_RP_NIR.dat'), usecols=(0,1)) bpm_img[bpm_loc[:,0].astype(int),bpm_loc[:,1].astype(int)] = 1. -# try : -# bpm_loc = np.loadtxt(vlt_sc.get_file_path('BP_MAP_RP_NIR.dat'), usecols=(0,1)) -# except IOError : -# # TODO: Do we need this anymore? Both the *.dat and *.fits.gz -# # files are present in the repo. -# msgs.warn('BP_MAP_RP_NIR.dat not present in the static database') -# bpm_fits = io.fits_open(vlt_sc.get_file_path('BP_MAP_RP_NIR.fits.gz')) -# # ToDo: this depends on datasec, biassec, specflip, and specaxis -# # and should become able to adapt to these parameters. -# # Flipping and shifting BPM to match the PypeIt format -# y_shift = -2 -# x_shift = 18 -# bpm_data = np.flipud(bpm_fits[0].data) -# y_len = len(bpm_data[:,0]) -# x_len = len(bpm_data[0,:]) -# bpm_data_pypeit = np.full( ((y_len+abs(y_shift)),(x_len+abs(x_shift))) , 0) -# bpm_data_pypeit[:-abs(y_shift),:-abs(x_shift)] = bpm_data_pypeit[:-abs(y_shift),:-abs(x_shift)] + bpm_data -# bpm_data_pypeit = np.roll(bpm_data_pypeit,-y_shift,axis=0) -# bpm_data_pypeit = np.roll(bpm_data_pypeit,x_shift,axis=1) -# filt_bpm = bpm_data_pypeit[1:y_len,1:x_len]>100. -# y_bpm, x_bpm = np.where(filt_bpm) -# bpm_loc = np.array([y_bpm,x_bpm]).T -# # NOTE: This directly access the path, but we shouldn't be doing that... -# np.savetxt(vlt_sc.path / 'BP_MAP_RP_NIR.dat', bpm_loc, fmt=['%d','%d']) -# finally : -# bpm_img[bpm_loc[:,0].astype(int),bpm_loc[:,1].astype(int)] = 1. return bpm_img @@ -548,8 +498,8 @@ def spec_min_max(self): Return the minimum and maximum spectral pixel expected for the spectral range of each order. """ - spec_max = np.asarray([1467,1502,1540, 1580,1620,1665,1720, 1770,1825,1895, 1966, 2000,2000,2000,2000,2000]) - spec_min = np.asarray([420 ,390 , 370, 345, 315, 285, 248, 210, 165, 115, 63, 10, 0, 0, 0, 0]) + spec_max = np.asarray([1477,1513,1547, 1588,1628,1682,1733,1795,1855,1930,2005,2040,2040,2040,2040,2040]) + spec_min = np.asarray([420 ,390 , 370, 345, 315, 285, 248, 210, 165, 115, 58, 5, 0, 0, 0, 0]) return np.vstack((spec_min, spec_max)) def order_platescale(self, order_vec, binning=None): @@ -569,12 +519,9 @@ def order_platescale(self, order_vec, binning=None): `numpy.ndarray`_: An array with the platescale for each order provided by ``order``. """ - # TODO: Either assume a linear trend or measure this - # X-shooter manual says, but gives no exact numbers per order. - # NIR: 52.4 pixels (0.210"/pix) at order 11 to 59.9 pixels (0.184"/pix) at order 26. - - # Right now I just assume a simple linear trend - plate_scale = 0.184 + (order_vec - 26)*(0.184-0.210)/(26 - 11) + # TODO: Figure out the order-dependence of the updated plate scale + # From the X-Shooter P113 manual, average over all orders. No order-dependent values given. + plate_scale = 0.245*np.ones_like(order_vec) return plate_scale @property @@ -624,6 +571,11 @@ def get_detector_par(self, det, hdu=None): # Binning # TODO: Could this be detector dependent?? binning = '1,1' if hdu is None else self.get_meta_value(self.get_headarr(hdu), 'binning') + + # Grab the gain and read noise from the header. + # If hdu not present, use typical defaults + gain = None if hdu is None else hdu[0].header['HIERARCH ESO DET OUT1 CONAD'] + ronoise = None if hdu is None else hdu[0].header['HIERARCH ESO DET OUT1 RON'] # Detector 1 detector_dict = dict( @@ -633,17 +585,17 @@ def get_detector_par(self, det, hdu=None): specaxis = 0, specflip = False, spatflip = False, - platescale = 0.16, # average from order 17 and order 30, see manual + platescale = 0.154, # average from order 17 and order 30, see manual darkcurr = 0.0, # e-/pixel/hour saturation = 65535., nonlinear = 0.86, mincounts = -1e10, numamplifiers = 1, - gain = np.atleast_1d(0.595), # FITS format is flipped: PrimaryHDU (2106, 4000) w/respect to Python - ronoise = np.atleast_1d(3.1), # raw unbinned images are (4000,2106) (spec, spat) - datasec=np.atleast_1d('[:,11:2058]'), # pre and oscan are in the spatial direction - oscansec=np.atleast_1d('[:,2059:2106]'), - ) + gain = np.atleast_1d(gain), + ronoise = np.atleast_1d(ronoise), + datasec=np.atleast_1d('[:,11:2058]'), # FITS format is flipped: PrimaryHDU (2106, 4000) w/respect to Python + oscansec=np.atleast_1d('[:,2059:2106]'), # raw unbinned images are (4000,2106) (spec, spat) + ) # pre and oscan are in the spatial direction return detector_container.DetectorContainer(**detector_dict) @classmethod @@ -808,15 +760,12 @@ def order_platescale(self, order_vec, binning=None): `numpy.ndarray`_: An array with the platescale for each order provided by ``order``. """ - # VIS has no binning, but for an instrument with binning we would do this binspectral, binspatial = parse.parse_binning(binning) - - # ToDO Either assume a linear trend or measure this - # X-shooter manual says, but gives no exact numbers per order. - # VIS: 65.9 pixels (0.167"/pix) at order 17 to 72.0 pixels (0.153"/pix) at order 30. - - # Right now I just assume a simple linear trend - plate_scale = 0.153 + (order_vec - 30)*(0.153-0.167)/(30 - 17) + + # TODO: Figure out the order-dependence of the updated plate scale + # From the X-Shooter P113 manual, average over all orders. No order-dependent values given. + plate_scale = 0.154*np.ones_like(order_vec) + return plate_scale*binspatial @property @@ -915,6 +864,10 @@ def get_detector_par(self, det, hdu=None): """ # Binning binning = '1,1' if hdu is None else self.get_meta_value(self.get_headarr(hdu), 'binning') + + # Grab the gain and read noise from the header. + gain = None if hdu is None else hdu[0].header['HIERARCH ESO DET OUT1 CONAD'] + ronoise = None if hdu is None else hdu[0].header['HIERARCH ESO DET OUT1 RON'] # Detector 1 detector_dict = dict( @@ -924,14 +877,14 @@ def get_detector_par(self, det, hdu=None): specaxis = 0, specflip = True, spatflip = True, - platescale = 0.161, # average from order 14 and order 24, see manual + platescale = 0.164, # average from order 14 and order 24, see manual darkcurr = 0.0, # e-/pixel/hour saturation = 65000., nonlinear = 0.86, mincounts = -1e10, numamplifiers = 1, - gain = np.atleast_1d(1.61), - ronoise = np.atleast_1d(2.60), + gain = np.atleast_1d(gain), + ronoise = np.atleast_1d(ronoise), datasec = np.atleast_1d('[:,49:2096]'), # '[49:2000,1:2999]', oscansec = np.atleast_1d('[:,1:48]'), # '[1:48, 1:2999]', ) @@ -1116,15 +1069,11 @@ def order_platescale(self, order_vec, binning = None): """ binspectral, binspatial = parse.parse_binning(binning) - # ToDO Either assume a linear trend or measure this - # X-shooter manual says, but gives no exact numbers per order. - # UVB: 65.9 pixels (0.167“/pix) at order 14 to 70.8 pixels (0.155”/pix) at order 24 - - # Assume a simple linear trend - plate_scale = 0.155 + (order_vec - 24)*(0.155-0.167)/(24 - 14) + # TODO: Figure out the order-dependence of the updated plate scale + # From the X-Shooter P113 manual, average over all orders. No order-dependent values given. + plate_scale = 0.164*np.ones_like(order_vec) - # Right now I just took the average - return np.full(self.norders, 0.161)*binspatial + return plate_scale*binspatial def bpm(self, filename, det, shape=None, msbias=None): """ diff --git a/setup.cfg b/setup.cfg index 65dedb90ca..c910a2c648 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,7 +33,7 @@ python_requires = >=3.10,<3.13 setup_requires = setuptools_scm include_package_data = True install_requires = - numpy>=1.23 + numpy>=1.23,<2.0.0 astropy>=6.0 extension-helpers>=0.1 packaging>=0.19 @@ -50,7 +50,7 @@ install_requires = qtpy>=2.0.1 pygithub bottleneck - pyqt6 + pyqt6<=6.7.0 scripts = bin/pypeit_c_enabled bin/pypeit_chk_plugins @@ -110,6 +110,7 @@ console_scripts = pypeit_arxiv_solution = pypeit.scripts.arxiv_solution:ArxivSolution.entry_point pypeit_cache_github_data = pypeit.scripts.cache_github_data:CacheGithubData.entry_point pypeit_clean_cache = pypeit.scripts.clean_cache:CleanCache.entry_point + pypeit_chk_flexure = pypeit.scripts.chk_flexure:ChkFlexure.entry_point pypeit_chk_for_calibs = pypeit.scripts.chk_for_calibs:ChkForCalibs.entry_point pypeit_chk_noise_1dspec = pypeit.scripts.chk_noise_1dspec:ChkNoise1D.entry_point pypeit_chk_noise_2dspec = pypeit.scripts.chk_noise_2dspec:ChkNoise2D.entry_point