diff --git a/README.md b/README.md index a8e50fdea..9d5953e1a 100644 --- a/README.md +++ b/README.md @@ -5,18 +5,18 @@ # Spectractor -The goal of Spectractor is to measure the atmospheric transmission and intempt extracting spectra from slitless spectrophotometric images. It has been optimized on CTIO images but can be configured to analyse any kind of slitless data that contains the order 0 and the order 1 of a spectrum. In particular it can be used to estimate the atmospheric transmission of the LSST site using the dedicated Auxiliary Telescope. +The goal of Spectractor is to measure the atmospheric transmission and attempt to extract spectra from slitless spectrophotometric images. It has been optimized on CTIO images but can be configured to analyse any kind of slitless data that contains the order 0 and the order 1 of a spectrum. In particular it can be used to estimate the atmospheric transmission of the LSST site using the dedicated Auxiliary Telescope. -Spectractor is structured in three subpackages: -- `spectractor.extractor`: extracts as most information as possible from a slitless data image, as the amplitude of the spectrum, the PSF evolution with the wavelength, the pixel to wavelength calibration, an estimate of the background under the spectrum, the position of the order 0; -- `spectractor.simulation`: contains all the tools to simulate a spectrogram, as the atmospheric transmission simulation, the inclusion of instrumental throughput, the simulation of mock slitless data images; +Spectractor is structured in three subpackages: +- `spectractor.extractor`: extracts as much information as possible from a slitless data image, such as the amplitude of the spectrum, the PSF evolution with the wavelength, the pixel to wavelength calibration, an estimate of the background under the spectrum, and the position of the order 0; +- `spectractor.simulation`: contains all the tools to simulate a spectrogram, such as atmospheric transmission simulation, the inclusion of instrumental throughput, the simulation of mock slitless data images; - `spectractor.fit`: compares the extracted data with simulations to estimate the atmospheric transmission and refine the spectral extraction. -Some submodules complete the structures with generic functions: -- `spectractor.parameters`: contains all the global parameters of Spectractor to set its general behaviour, the instrumental characteritics, etc; +Some submodules complete the structure with generic functions: +- `spectractor.parameters`: contains all the global parameters of Spectractor to set its general behaviour, the instrumental characteristics, etc; - `spectractor.config`: tools to read config `.ini` text files and set the global parameters; - `spectractor.logbook`: tools to read logbook `.csv` text files and get some metadata relative to the data images that are not contained in the header; -- `spectractor.tools`: contains generic functions shared by all the subpackages (fitting procedures, plotting functions, etc). +- `spectractor.tools`: contains generic functions shared by all the subpackages (fitting procedures, plotting functions, etc). ## Installation @@ -50,9 +50,9 @@ Spectractor is able to perform parameter fits using the MCMC library [emcee](htt ## Basic extraction The main file is `spectractor/extractor/extractor.py` with the function `Spectractor`. It extracts the spectrum from a science data image (deflatted, debiased), given: -- the path to the FITS image from which to extract the image, +- the path to the FITS image from which to extract the image, - the path of the output directory to save the extracted spectrum (created automatically if it does not exist yet), -- the rough pr exact position of the object in the image (in pixels), +- the rough or exact position of the object in the image (in pixels), - the name of the disperser (as it is named in the `spectractor/extractor/dispersers/` folder), - the name of the config .ini file, - optionally the name of the target (to search for the extra-atmospheric spectrum if available). @@ -66,7 +66,7 @@ config = "./config/ctio.ini" target = "HD111980" ``` -Then the spectrum is simply extracted from the image and saved in a new fits file using the `Spectractor` function: +Then the spectrum is simply extracted from the image and saved in a new FITS file using the `Spectractor` function: ``` spectrum = Spectractor(filename, output_directory, guess=guess, target_label=target, disperser_label=disperser_label, config=config) ``` diff --git a/spectractor/astrometry.py b/spectractor/astrometry.py index 2d4170f15..462c55c4c 100644 --- a/spectractor/astrometry.py +++ b/spectractor/astrometry.py @@ -371,7 +371,7 @@ def load_gaia_catalog_around_target(self): def load_sources_from_file(self): """Load the sources from the class associated self.sources_file_name file. - By default, the creation of an Astrometry class instance try to load the default + By default, the creation of an Astrometry class instance tries to load the default source file if it exists. Returns @@ -397,7 +397,7 @@ def load_sources_from_file(self): return sources def get_target_pixel_position(self): - """Gives the principal targetted object position in pixels in the image given the WCS. + """Gives the principal targeted object position in pixels in the image given the WCS. The object proper motion is taken into account. Returns @@ -495,7 +495,7 @@ def get_quad_stars_pixel_positions(self): if np.sum(coord) > 0: coords.append(coord) if len(coords) < 4: - self.my_logger.warning(f"\n\tOnly {len(coords)} calibration stars has been extracted from " + self.my_logger.warning(f"\n\tOnly {len(coords)} calibration stars have been extracted from " f"{self.match_file_name}, with positions {coords}. " f"A quad of at least 4 stars is expected. " f"Please check {self.match_file_name}.") @@ -754,7 +754,7 @@ def plot_astrometry_shifts(self, vmax=3, margin=parameters.CCD_IMSIZE): def set_constraints(self, min_stars=100, flux_log10_threshold=0.1, min_range=3 * u.arcsec, max_range=5 * u.arcmin, max_sep=1 * u.arcsec): - """Gives a boolean array for sources that respect certain criterai (see below). + """Gives a boolean array for sources that respect certain criteria (see below). Parameters ---------- diff --git a/spectractor/config.py b/spectractor/config.py index bd0cbdb53..ad8613e30 100644 --- a/spectractor/config.py +++ b/spectractor/config.py @@ -20,7 +20,7 @@ def from_config_to_dict(path): - """Convert config file keywords into dictionnary. + """Convert config file keywords into dictionary. Parameters ---------- @@ -243,7 +243,6 @@ def set_logger(logger): >>> parameters.DEBUG = True >>> test = Test() >>> test.log() - """ my_logger = logging.getLogger(logger) my_format = "%(asctime)-20s %(name)-10s %(funcName)-20s %(levelname)-6s %(message)s" diff --git a/spectractor/config/auxtel.ini b/spectractor/config/auxtel.ini index 938c40f52..531bd7a7b 100644 --- a/spectractor/config/auxtel.ini +++ b/spectractor/config/auxtel.ini @@ -38,7 +38,7 @@ OBS_EPOCH = J2000.0 # observed object to choose between STAR, HG-AR, MONOCHROMATOR OBS_OBJECT_TYPE = STAR # full instrument transmission file -OBS_FULL_INSTRUMENT_TRANSMISSON = multispectra_holo4_003_HD142331_AuxTel_throughput.txt +OBS_FULL_INSTRUMENT_TRANSMISSION = multispectra_holo4_003_HD142331_AuxTel_throughput.txt # systematics on the instrument transmission OBS_TRANSMISSION_SYSTEMATICS = 0.005 # Camera (x,y) rotation angle with respect to (north-up, east-left) system @@ -121,7 +121,7 @@ PSF_PIXEL_STEP_TRANSVERSE_FIT = 50 PSF_FWHM_CLIP = 2 [detection line algorithm parameters] -# order of the background polynome to fit +# order of the background polynomial to fit CALIB_BGD_ORDER = 3 # half range to look for local extrema in pixels around tabulated line values CALIB_PEAK_WIDTH = 7 diff --git a/spectractor/config/auxtel_quicklook.ini b/spectractor/config/auxtel_quicklook.ini index ab50ea830..f59abbe01 100644 --- a/spectractor/config/auxtel_quicklook.ini +++ b/spectractor/config/auxtel_quicklook.ini @@ -28,7 +28,7 @@ OBS_EPOCH = J2000.0 # observed object to choose between STAR, HG-AR, MONOCHROMATOR OBS_OBJECT_TYPE = STAR # full instrument transmission file -OBS_FULL_INSTRUMENT_TRANSMISSON = multispectra_holo4_003_HD142331_AuxTel_throughput.txt +OBS_FULL_INSTRUMENT_TRANSMISSION = multispectra_holo4_003_HD142331_AuxTel_throughput.txt # systematics on the instrument transmission OBS_TRANSMISSION_SYSTEMATICS = 0.005 # Camera (x,y) rotation angle with respect to (north-up, east-left) system @@ -109,7 +109,7 @@ PSF_FIT_REG_PARAM = 1 PSF_PIXEL_STEP_TRANSVERSE_FIT = 50 [detection line algorithm parameters] -# order of the background polynome to fit +# order of the background polynomial to fit CALIB_BGD_ORDER = 3 # half range to look for local extrema in pixels around tabulated line values CALIB_PEAK_WIDTH = 7 diff --git a/spectractor/config/ctio.ini b/spectractor/config/ctio.ini index 35a285446..c95031093 100644 --- a/spectractor/config/ctio.ini +++ b/spectractor/config/ctio.ini @@ -34,7 +34,7 @@ OBS_EPOCH = J2000.0 # observed object to choose between STAR, HG-AR, MONOCHROMATOR OBS_OBJECT_TYPE = STAR # full instrument transmission file -OBS_FULL_INSTRUMENT_TRANSMISSON = multispectra_Thor300_HD111980_CTIO_throughput_prod7.5.4.txt +OBS_FULL_INSTRUMENT_TRANSMISSION = multispectra_Thor300_HD111980_CTIO_throughput_prod7.5.4.txt # systematics on the instrument transmission OBS_TRANSMISSION_SYSTEMATICS = 0.0 # Camera (x,y) rotation angle with respect to (north-up, east-left) system @@ -113,7 +113,7 @@ PSF_PIXEL_STEP_TRANSVERSE_FIT = 10 PSF_FWHM_CLIP = 2 [detection line algorithm parameters] -# order of the background polynome to fit +# order of the background polynomial to fit CALIB_BGD_ORDER = 3 # half range to look for local extrema in pixels around tabulated line values CALIB_PEAK_WIDTH = 7 diff --git a/spectractor/config/default.ini b/spectractor/config/default.ini index 714e74dc6..18f21d48d 100644 --- a/spectractor/config/default.ini +++ b/spectractor/config/default.ini @@ -38,7 +38,7 @@ OBS_EPOCH = J2000.0 # observed object to choose between STAR, HG-AR, MONOCHROMATOR OBS_OBJECT_TYPE = STAR # full instrument transmission file -OBS_FULL_INSTRUMENT_TRANSMISSON = ctio_throughput_300517_v2.txt +OBS_FULL_INSTRUMENT_TRANSMISSION = ctio_throughput_300517_v2.txt # systematics on the instrument transmission OBS_TRANSMISSION_SYSTEMATICS = 0 @@ -125,7 +125,7 @@ PSF_FIT_REG_PARAM = 0.01 PSF_POLY_TYPE = polynomial [detection line algorithm parameters] -# order of the background polynome to fit +# order of the background polynomial to fit CALIB_BGD_ORDER = 3 # half range to look for local extrema in pixels around tabulated line values CALIB_PEAK_WIDTH = 7 diff --git a/spectractor/config/lpnhe.ini b/spectractor/config/lpnhe.ini index 9aab0dde9..46141629a 100644 --- a/spectractor/config/lpnhe.ini +++ b/spectractor/config/lpnhe.ini @@ -89,7 +89,7 @@ PSF_FIT_REG_PARAM = 0.01 PSF_PIXEL_STEP_TRANSVERSE_FIT = 10 [detection line algorithm parameters] -# order of the background polynome to fit +# order of the background polynomial to fit CALIB_BGD_ORDER = 3 # half range to look for local extrema in pixels around tabulated line values CALIB_PEAK_WIDTH = 3 diff --git a/spectractor/config/stardice.ini b/spectractor/config/stardice.ini index 22987e448..b51959182 100644 --- a/spectractor/config/stardice.ini +++ b/spectractor/config/stardice.ini @@ -34,7 +34,7 @@ OBS_EPOCH = J2000.0 # observed object to choose between STAR, HG-AR, MONOCHROMATOR OBS_OBJECT_TYPE = STAR # full instrument transmission file -OBS_FULL_INSTRUMENT_TRANSMISSON = StarDice_EMPTY_response_75um_pinhole.txt +OBS_FULL_INSTRUMENT_TRANSMISSION = StarDice_EMPTY_response_75um_pinhole.txt # systematics on the instrument transmission OBS_TRANSMISSION_SYSTEMATICS = 0.005 # Camera (x,y) rotation angle with respect to (north-up, east-left) system @@ -117,7 +117,7 @@ PSF_PIXEL_STEP_TRANSVERSE_FIT = 10 PSF_FWHM_CLIP = 2 [detection line algorithm parameters] -# order of the background polynome to fit +# order of the background polynomial to fit CALIB_BGD_ORDER = 3 # half range to look for local extrema in pixels around tabulated line values CALIB_PEAK_WIDTH = 7 diff --git a/spectractor/extractor/chromaticpsf.py b/spectractor/extractor/chromaticpsf.py index 23fe5e124..a414d1a6a 100644 --- a/spectractor/extractor/chromaticpsf.py +++ b/spectractor/extractor/chromaticpsf.py @@ -113,7 +113,7 @@ def init_from_table(self, table, saturation=None): self.saturation = saturation if saturation is None: self.saturation = 1e20 - self.my_logger.warning(f"\n\tSaturation level should be given to instanciate the ChromaticPSF " + self.my_logger.warning(f"\n\tSaturation level should be given to instantiate the ChromaticPSF " f"object. self.saturation is set arbitrarily to 1e20. Good luck.") for name in self.psf.params.labels: if "amplitude" in name: @@ -619,8 +619,8 @@ def convolve_psf_cube_masked(psf_cube_masked): @staticmethod def set_rectangular_boundaries(psf_cube_masked): - """Compute the ChromaticPSF computation boundaries, as a dictionnary of integers giving - the `"xmin"`, `"xmax"`, `"ymin"` and `"ymax"` edges where to compute the PSF for each wavelength. + """Compute the ChromaticPSF computation boundaries, as a dictionary of integers giving + the `"xmin"`, `"xmax"`, `"ymin"` and `"ymax"` edges where the PSF is computed for each wavelength. True regions are rectangular after this operation. The `psf_cube_masked` cube is updated accordingly and returned. Parameters @@ -631,7 +631,7 @@ def set_rectangular_boundaries(psf_cube_masked): Returns ------- boundaries: dict - The dictionnary of PSF edges per wavelength. + The dictionary of PSF edges per wavelength. psf_cube_masked: np.ndarray Updated cube of boolean values where `psf_cube` cube is positive, eventually convolved. @@ -684,12 +684,12 @@ def set_rectangular_boundaries(psf_cube_masked): return boundaries, psf_cube_masked def get_sparse_indices(self, boundaries): - """Methods that returns the indices to build sparse matrices from rectangular `boundaries`. + """Method that returns the indices to build sparse matrices from rectangular `boundaries`. Parameters ---------- boundaries: dict - The dictionnary of PSF edges per wavelength. + The dictionary of PSF edges per wavelength. Returns ------- @@ -1813,7 +1813,7 @@ def fit_chromatic_psf(self, data, mask=None, bgd_model_func=None, data_errors=No # first fit order 0 terms w.my_logger.info("\n\tFit order 0 parameters...") fixed_default = np.copy(w.params.fixed) - # fix higher order coefficients of polynomes + # fix higher-order coefficients of polynomials for k in range(w.params.ndim): if "_0" not in w.params.labels[k] and not w.params.fixed[k]: w.params.fixed[k] = True # _k parameters that are yet fixed diff --git a/spectractor/extractor/extractor.py b/spectractor/extractor/extractor.py index 72647868f..32e4016d6 100644 --- a/spectractor/extractor/extractor.py +++ b/spectractor/extractor/extractor.py @@ -122,7 +122,7 @@ def __init__(self, spectrum, amplitude_priors_method="noprior", verbose=False, p params.fixed[params.get_index("A2")] = (not spectrum.disperser.flat_ratio_order_2over1) and (not ("A2_T" in spectrum.header)) if spectrum.spectrogram_starfield is None: params.fixed[params.get_index("A_star")] = True # Astar - params.fixed[params.get_index("D_CCD [mm]")] = True # D2CCD: spectrogram can not tell something on this parameter: rely on calibrate_spectrum + params.fixed[params.get_index("D_CCD [mm]")] = True # D2CCD: spectrogram cannot constrain this parameter: rely on calibrate_spectrum params.fixed[params.get_index("shift_x [pix]")] = True # delta x: if False, extracted spectrum is biased compared with truth params.fixed[params.get_index("shift_y [pix]")] = False # delta y params.fixed[params.get_index("angle [deg]")] = False # angle diff --git a/spectractor/extractor/images.py b/spectractor/extractor/images.py index 425e4bdcb..1b58309b8 100644 --- a/spectractor/extractor/images.py +++ b/spectractor/extractor/images.py @@ -217,17 +217,17 @@ def __init__(self, file_name, *, target_label="", disperser_label="", QN3_2 = Line(539, atmospheric=True, label=r'$QN3$', label_pos=[0.007, 0.02], width_bounds=[0.1, 3], use_for_calibration=True) # QN 3rd band right edge QN3_3 = Line(531, atmospheric=True, label=r'$QN3$', label_pos=[0.007, 0.02], width_bounds=[1, 50], use_for_calibration=False) # QN 3rd band center QN4_1 = Line(620, atmospheric=True, label=r'$QN4$', label_pos=[0.007, 0.02], width_bounds=[0.1, 3], use_for_calibration=True) # QN 4th band left edge - QN = [QN1_1, QN1_2, QN2_1, QN2_2, QN3_1, QN3_2, QN4_1, QN1_3, QN2_3, QN3_3] + QN = [QN1_1, QN1_2, QN2_1, QN2_2, QN3_1, QN3_2, QN4_1, QN1_3, QN2_3, QN3_3] for line in self.target.lines.lines: - # TODO: put this has an attribute of the disperser of filter + # TODO: make this an attribute of the disperser or filter if 410 < line.wavelength < 415 or 475 < line.wavelength < 500 or 520 < line.wavelength < 545 or 610 < line.wavelength: line.use_for_calibration = False - + for l in QN: self.target.lines.lines.append(l) _ = self.target.lines.sort_lines() self.target.lines.sort_lines() - + def rebin(self): """Rebin the image and reset some related parameters. @@ -409,7 +409,7 @@ def check_statistical_error(self): in terms of gain and read-out noise. A linear model is fitted to the squared pixel uncertainty values with respect to the pixel data values. - The slop gives the gain value and the intercept gives the read-out noise value. + The slope gives the gain value and the intercept gives the read-out noise value. Returns ------- @@ -794,7 +794,7 @@ def load_AUXTEL_image(image): # pragma: no cover if not np.isclose(rotation_wcs % 360, parameters.OBS_CAMERA_ROTATION % 360, atol=2): image.my_logger.warning(f"\n\tWCS rotation angle is {rotation_wcs} degree while " f"parameters.OBS_CAMERA_ROTATION={parameters.OBS_CAMERA_ROTATION} degree. " - f"\nBoth differs by more than 2 degree... bug ?") + f"\nDifference is more than 2 degrees... bug ?") parameters.OBS_ALTITUDE = float(image.header['OBS-ELEV']) / 1000 parameters.OBS_LATITUDE = image.header['OBS-LAT'] image.read_out_noise = 8.5 * np.ones_like(image.data) @@ -840,7 +840,7 @@ def load_STARDICE_image(image): # pragma: no cover # with spectrogram nearly horizontal and on the right of central star image.data = image.data[::-1, ::-1] image.airmass = 1/np.cos(np.radians(90-image.header['MOUNTALT'])) - + image.my_logger.info('\n\tImage loaded') # compute CCD gain map image.gain = float(parameters.CCD_GAIN) * np.ones_like(image.data) @@ -859,11 +859,11 @@ def load_STARDICE_image(image): # pragma: no cover if "PC2_1" in image.header: rotation_wcs = 180 / np.pi * np.arctan2(-hdu_list[0].header["PC2_1"]/hdu_list[0].header["CDELT2"], hdu_list[0].header["PC1_1"]/hdu_list[0].header["CDELT1"]) atol = 0.02 - print("RORATION WCS :", rotation_wcs) + print("ROTATION WCS :", rotation_wcs) if not np.isclose(rotation_wcs % 360, parameters.OBS_CAMERA_ROTATION % 360, atol=atol): image.my_logger.warning(f"\n\tWCS rotation angle is {rotation_wcs} degrees while " f"parameters.OBS_CAMERA_ROTATION={parameters.OBS_CAMERA_ROTATION} degrees. " - f"\nBoth differs by more than {atol} degrees... bug ?") + f"\nDifference is more than {atol} degrees... bug ?") image.read_out_noise = 8.5 * np.ones_like(image.data) image.compute_parallactic_angle() @@ -872,7 +872,7 @@ def load_STARDICE_image(image): # pragma: no cover def find_target(image, guess=None, rotated=False, widths=[parameters.XWINDOW, parameters.YWINDOW]): """Find the target in the Image instance. - The object is search in a windows of size defined by the XWINDOW and YWINDOW parameters, + The object is searched in a window of size defined by the XWINDOW and YWINDOW parameters, using two iterative fits of a PSF model. User must give a guess array in the raw image. @@ -991,7 +991,7 @@ def find_target(image, guess=None, rotated=False, widths=[parameters.XWINDOW, pa elif parameters.SPECTRACTOR_FIT_TARGET_CENTROID == "WCS" and not rotated: pass else: - raise ValueError(f"For unrotated images, parameters.SPECTRACTOR_FIT_TARGET_CENTROID muste be either: " + raise ValueError(f"For unrotated images, parameters.SPECTRACTOR_FIT_TARGET_CENTROID must be either: " f"guess, fit or WCS. Got {parameters.SPECTRACTOR_FIT_TARGET_CENTROID}.") image.my_logger.info(f'\n\tX,Y target position in pixels: {theX:.3f},{theY:.3f}') if rotated: @@ -1051,7 +1051,7 @@ def find_target_init(image, guess, rotated=False, widths=[parameters.XWINDOW, pa x0 = int(guess[0]) y0 = int(guess[1]) Dx, Dy = widths - + # crop parameters subYmin, subYmax = y0 - Dy, y0 + Dy subXmin, subXmax = x0 - Dx, x0 + Dx @@ -1060,7 +1060,7 @@ def find_target_init(image, guess, rotated=False, widths=[parameters.XWINDOW, pa # verify if the sub image is out of bounds if subYmin < 0 or subXmin < 0 or subYmax >= sizeY or subXmax >= sizeX: - + old_subYmin, old_subYmax, old_subXmin, old_subXmax = subYmin, subYmax, subXmin, subXmax subYmin, subXmin = max(0, subYmin), max(0, subXmin) @@ -1293,7 +1293,7 @@ def compute_rotation_angle_hessian(image, angle_range=(-10, 10), width_cut=param angle_range: (float, float) Don't consider pixel with Hessian angle outside this range (default: (-10,10)). width_cut: int - Half with of the image to consider in height (default: parameters.YWINDOW). + Half width of the image to consider in height (default: parameters.YWINDOW). edges: (int, int) Minimum and maximum pixel on the right edge (default: (0, parameters.CCD_IMSIZE)). margin_cut: int @@ -1305,7 +1305,7 @@ def compute_rotation_angle_hessian(image, angle_range=(-10, 10), width_cut=param Returns ------- theta: float - The median value of the histogram of angles deduced with the Hessian of the pixels (in degree). + The median value of the histogram of angles deduced with the Hessian of the pixels (in degrees). Examples -------- @@ -1403,7 +1403,7 @@ def compute_rotation_angle_hessian(image, angle_range=(-10, 10), width_cut=param def turn_image(image): """Compute the rotation angle using the Hessian algorithm and turn the image. - The results are stored in Image.data_rotated and Image.stat_errors_rotated. + The results are stored in Image.data_rotated and Image.err_rotated. Parameters ---------- diff --git a/spectractor/extractor/psf.py b/spectractor/extractor/psf.py index 3ba408325..51b38fa54 100644 --- a/spectractor/extractor/psf.py +++ b/spectractor/extractor/psf.py @@ -1440,7 +1440,7 @@ class PSF: (in this order) and "saturation" parameter as the last parameter. "amplitude", "x_c" and "y_c" stands respectively for the general amplitude of the model, the position along the dispersion axis and the transverse position with respect to the dispersion axis (assumed to be the X axis). - Last "saturation" parameter must be express in the same units as the signal to model and as the "amplitude" + Last "saturation" parameter must be expressed in the same units as the signal to model and as the "amplitude" parameter. The PSF models must be normalized to one in total flux divided by the first parameter (amplitude). Then the PSF model integral is equal to the "amplitude" parameter. @@ -1610,9 +1610,9 @@ def evaluate(self, pixels, values=None): Parameters ---------- pixels: list - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. values: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. Returns @@ -1688,16 +1688,16 @@ def jacobian(self, pixels, params, epsilon=None, model_input=None, analytical=Tr Parameters ---------- pixels: array_like - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. params: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. epsilon: array_like, optional The array of small steps to compute the partial derivatives of the model if analytical=False (default: None). model_input: array_like, optional A model input as a list with (x, model, model_err) to avoid an additional call to simulate() if analytical=False (default: None). analytical: bool, optional - If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differenciations + If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differentiations with steps given by epsilon argument (default: True). Returns @@ -1801,9 +1801,9 @@ def evaluate(self, pixels, values=None): Parameters ---------- pixels: list - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. values: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. Returns @@ -1863,16 +1863,16 @@ def jacobian(self, pixels, params, epsilon=None, model_input=None, analytical=Tr Parameters ---------- pixels: array_like - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. params: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. epsilon: array_like, optional The array of small steps to compute the partial derivatives of the model if analytical=False (default: None). model_input: array_like, optional A model input as a list with (x, model, model_err) to avoid an additional call to simulate() if analytical=False (default: None). analytical: bool, optional - If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differenciations + If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differentiations with steps given by epsilon argument (default: True). Returns @@ -1980,9 +1980,9 @@ def evaluate(self, pixels, values=None): Parameters ---------- pixels: list - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. values: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. Returns @@ -2043,16 +2043,16 @@ def jacobian(self, pixels, params, epsilon=None, model_input=None, analytical=Tr Parameters ---------- pixels: array_like - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. params: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. epsilon: array_like, optional The array of small steps to compute the partial derivatives of the model if analytical=False (default: None). model_input: array_like, optional A model input as a list with (x, model, model_err) to avoid an additional call to simulate() if analytical=False (default: None). analytical: bool, optional - If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differenciations + If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differentiations with steps given by epsilon argument (default: True). Returns @@ -2162,9 +2162,9 @@ def evaluate(self, pixels, values=None): Parameters ---------- pixels: list - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. values: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. Returns @@ -2226,16 +2226,16 @@ def jacobian(self, pixels, params, epsilon=None, model_input=None, analytical=Tr Parameters ---------- pixels: array_like - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. params: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. epsilon: array_like, optional The array of small steps to compute the partial derivatives of the model if analytical=False (default: None). model_input: array_like, optional A model input as a list with (x, model, model_err) to avoid an additional call to simulate() if analytical=False (default: None). analytical: bool, optional - If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differenciations + If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differentiations with steps given by epsilon argument (default: True). Returns @@ -2364,9 +2364,9 @@ def evaluate(self, pixels, values=None): Parameters ---------- pixels: list - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. values: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. Returns @@ -2448,16 +2448,16 @@ def jacobian(self, pixels, params, epsilon=None, model_input=None, analytical=Fa Parameters ---------- pixels: array_like - List containing the X abscisse 2D array and the Y abscisse 2D array. + List containing the X abscissa 2D array and the Y abscissa 2D array. params: array_like - The parameter array. If None, the array used to instanciate the class is taken. + The parameter array. If None, the array used to instantiate the class is taken. If given, the class instance parameter array is updated. epsilon: array_like, optional The array of small steps to compute the partial derivatives of the model if analytical=False (default: None). model_input: array_like, optional A model input as a list with (x, model, model_err) to avoid an additional call to simulate() if analytical=False (default: None). analytical: bool, optional - If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differenciations + If True, use analytical derivatives to compute Jacobian operator. Otherwise use numerical differentiations with steps given by epsilon argument (default: True). Returns diff --git a/spectractor/extractor/spectrum.py b/spectractor/extractor/spectrum.py index b86db57f5..eac355dfc 100644 --- a/spectractor/extractor/spectrum.py +++ b/spectractor/extractor/spectrum.py @@ -405,7 +405,7 @@ def plot_spectrum(self, ax=None, xlim=None, live_fit=False, label='', force_line label: str Label for the legend (default: ''). xlim: list, optional - List of minimum and maximum abscisses (default: None) + List containing a minimum and a maximum x value (default: None) live_fit: bool, optional If True the spectrum is plotted in live during the fitting procedures (default: False). @@ -529,7 +529,7 @@ def plot_spectrum_summary(self, xlim=None, figsize=(12, 12), save_as=''): Parameters ---------- xlim: list, optional - List of minimum and maximum abscisses (default: None). + List containing a minimum and a maximum x value (default: None). figsize: tuple Figure size (default: (12, 12)). save_as : str, optional diff --git a/spectractor/extractor/targets.py b/spectractor/extractor/targets.py index 8256ae7b9..e48099333 100644 --- a/spectractor/extractor/targets.py +++ b/spectractor/extractor/targets.py @@ -31,7 +31,7 @@ else: _USE_NEW_SIMBAD = True _SIMBAD_VOTABLE_FIELDS = ('U', 'B', 'V', 'R', 'I', 'J', 'sp_type', - 'parallax', 'propermotions', 'rvz_redshift', "IDS", "ids") + 'parallax', 'propermotions', 'rvz_redshift', "IDS", "ids") try: from gaiaspec import getGaia @@ -59,7 +59,7 @@ def load_target(label, verbose=False): """Load the target properties according to the type set by parameters.OBS_OBJECT_TYPE. Currently, the type can be either "STAR", "HG-AR" or "MONOCHROMATOR". The label parameter gives the - name of the source and allows to load its specific properties. + name of the source and allows loading its specific properties. Parameters ---------- @@ -343,8 +343,8 @@ def load(self): self.redshift = float(self.simbad_table[redshift_key][0]) else: self.redshift = 0 - self.get_radec_position_after_pm(self.simbad_table, - date_obs="J2000", + self.get_radec_position_after_pm(self.simbad_table, + date_obs="J2000", date_reference = date_reference) self.load_spectra() @@ -420,7 +420,7 @@ def load_calspec(self): def load_gaia(self): """ Load the spectrum from the Gaia database. - + Examples -------- >>> s = Star('HD111980') @@ -470,7 +470,7 @@ def load_emission_spectrum(self, hydrogen_only_flag): def load_ned(self): """ Load the spectrum from NED database. - + Examples -------- >>> s = Star('3C273') @@ -583,8 +583,8 @@ def build_sed(self, index=0): fill_value=0.) else: self.sed_err = interp1d(self.wavelengths[index], self.spectra_err[index], kind='linear', bounds_error=False, - fill_value=10*np.max(self.spectra_err[index])) - + fill_value=10*np.max(self.spectra_err[index])) + def plot_spectra(self): """ Plot the spectra stored in the self.spectra list. diff --git a/spectractor/fit/fitter.py b/spectractor/fit/fitter.py index 5f15f5bd0..a3a406aac 100644 --- a/spectractor/fit/fitter.py +++ b/spectractor/fit/fitter.py @@ -583,7 +583,7 @@ def write_fitparameter_json(json_filename, params, extra=None): Returns ------- jsontxt: str - The JSON dictionnary as string. + The JSON dictionary as a string. Examples -------- @@ -600,7 +600,7 @@ def write_fitparameter_json(json_filename, params, extra=None): >>> os.remove(params.json_filename) """ if json_filename == "": - raise ValueError("Must provide attribute a JSON filename.") + raise ValueError("Must provide a JSON filename.") if extra: params.extra = extra jsontxt = json.dumps(params.__dict__, cls=NumpyArrayEncoder) @@ -770,7 +770,7 @@ def simulate(self, *p): Returns ------- x: array_like - The abscisse of the model prediction. + The abscissa of the model prediction. model: array_like The model prediction. model_err: array_like @@ -1279,18 +1279,18 @@ def save_gradient_descent(self): def plot_triangle_fisher(self, nsamples=10000, max_params=8): """Plot a triangle (corner) plot of probability contours and distributions using Fisher matrix method. - - This method generates samples from a multivariate normal distribution based on the parameter + + This method generates samples from a multivariate normal distribution based on the parameter covariance matrix and creates a corner plot showing 1D and 2D marginal distributions. - + Parameters ---------- nsamples: int, optional Number of samples to generate from the multivariate normal distribution (default: 10000). max_params: int, optional - Maximum number of parameters to display. Only the first max_params free parameters + Maximum number of parameters to display. Only the first max_params free parameters will be shown (default: 8). - + Examples -------- >>> from spectractor.fit.fitter import FitParameters, FitWorkspace @@ -1298,7 +1298,7 @@ def plot_triangle_fisher(self, nsamples=10000, max_params=8): >>> params.cov = np.array([[1, -0.5, 0], [-0.5, 1, -1], [0, -1, 1]]) >>> w = FitWorkspace(params=params, filename="test.fits") >>> w.plot_triangle_fisher(nsamples=1000, max_params=3) - + Notes ----- This method requires the `corner` package to be installed. @@ -1310,56 +1310,56 @@ def plot_triangle_fisher(self, nsamples=10000, max_params=8): self.my_logger.warning("Package 'corner' not installed. Cannot create triangle plot. " "Install it with: pip install corner") return - + # Get free parameters indices ipar = self.params.get_free_parameters() - + # Limit to max_params parameters n_params = min(len(ipar), max_params) ipar = ipar[:n_params] - + if len(ipar) == 0: self.my_logger.warning("No free parameters to plot.") return - + # Check that covariance matrix is available if self.params.cov.size == 0: self.my_logger.warning("Covariance matrix not available. Run fit first.") return - + # Extract mean values and covariance matrix for free parameters mean_values = self.params.values[ipar] cov_matrix = self.params.cov[:n_params, :n_params] - + # Check if covariance matrix is positive definite eigenvalues = np.linalg.eigvals(cov_matrix) if np.any(eigenvalues <= 0): self.my_logger.warning("Covariance matrix is not positive definite. " "Cannot generate samples.") return - + # Generate samples from multivariate normal distribution samples = np.random.multivariate_normal(mean_values, cov_matrix, size=nsamples) - + # Get axis names for the selected parameters axis_names = [self.params.axis_names[i] for i in ipar] - + # Create corner plot - fig = corner.corner(samples, labels=axis_names, + fig = corner.corner(samples, labels=axis_names, quantiles=[0.16, 0.5, 0.84], show_titles=True, title_kwargs={"fontsize": 12}, truths=mean_values) - + # Save figure if requested if (parameters.SAVE or parameters.LSST_SAVEFIGPATH) and self.filename != "": # pragma: no cover figname = os.path.splitext(self.filename)[0] + "_triangle.pdf" self.my_logger.info(f"\n\tSave triangle plot {figname}.") fig.savefig(figname, dpi=100, bbox_inches='tight') - + if parameters.DISPLAY: # pragma: no cover plt.show() - + return fig @@ -1406,7 +1406,7 @@ def gradient_descent(fit_workspace, niter=10, xtol=1e-3, ftol=1e-3, with_line_se raise TypeError(f"Type of fit_workspace.W is {type(W)}. It must be a np.ndarray.") # Jacobian J = fit_workspace.jacobian(tmp_params, model_input=[tmp_lambdas, tmp_model, tmp_model_err]) - # remove parameters with unexpected null Jacobian vectors or that are degenerated + # remove parameters with unexpected null Jacobian vectors or that are degenerate J_vectors = [np.array(J[ip]).ravel() for ip in range(J.shape[0])] J_norms = [np.linalg.norm(J_vectors[ip]) for ip in range(J.shape[0])] for ip in range(J.shape[0]): @@ -1429,10 +1429,10 @@ def gradient_descent(fit_workspace, niter=10, xtol=1e-3, ftol=1e-3, with_line_se ipar = np.delete(ipar, list(ipar).index(jp)) fit_workspace.params.fixed[jp] = True my_logger.warning( - f"\n\tStep {i}: {fit_workspace.params.labels[jp]} is degenerated with {fit_workspace.params.labels[ip]}; " + f"\n\tStep {i}: {fit_workspace.params.labels[jp]} is degenerate with {fit_workspace.params.labels[ip]}; " f"parameter {fit_workspace.params.labels[jp]} is fixed at its last known current value ({tmp_params[jp]}).") continue - # remove fixed and degenerated parameters; then transpose + # remove fixed and degenerate parameters; then transpose J = J[ipar].T # compute J.T @ W @ J matrix and invert it @@ -1865,7 +1865,7 @@ def simulate(self, log10_r): A = cov @ (self.w.M.T @ (self.w.W * self.w.data) + reg * self.w.Q_dot_A0) else: A = cov @ (self.w.M.T @ (self.w.W @ self.w.data) + reg * self.w.Q_dot_A0) - + if A.ndim == 2: # ndim == 2 when A comes from a sparse matrix computation A = np.asarray(A).reshape(-1) diff = self.w.data - self.w.M @ A diff --git a/spectractor/fit/mcmc.py b/spectractor/fit/mcmc.py index 75abd2be3..02cbf860d 100644 --- a/spectractor/fit/mcmc.py +++ b/spectractor/fit/mcmc.py @@ -99,7 +99,7 @@ def getTotal(self): def normalize(self): self.total = self.getTotal() if self.total == 0: - sys.exit('Warning! Sum of likelihood is zero: can not normalize grid {}.'.format(self.axis_names)) + sys.exit('Warning! Sum of likelihood is zero: cannot normalize grid {}.'.format(self.axis_names)) else: self.grid = self.grid / self.total return self.total diff --git a/spectractor/parameters.py b/spectractor/parameters.py index a29fefbf2..34b32bb26 100644 --- a/spectractor/parameters.py +++ b/spectractor/parameters.py @@ -27,7 +27,7 @@ def __getattr__(name): # Exclude pytest_plugins to avoid pytest trying to load this module as a plugin if name == 'pytest_plugins': raise AttributeError(f"module 'parameters' has no attribute '{name}'") - + if name in locals(): return locals()[name] else: @@ -40,7 +40,7 @@ def __getattr__(name): SPECTRACTOR_DECONVOLUTION_PSF2D = True # deconvolve spectrogram with simple 2D PSF analysis: False, True SPECTRACTOR_DECONVOLUTION_FFM = True # deconvolve spectrogram with full forward model: False, True SPECTRACTOR_DECONVOLUTION_SIGMA_CLIP = 20 # value of sigma clip parameter for the spectractor deconvolution process PSF2D and FFM -SPECTRACTOR_BACKGROUND_SUBTRACTION = True #if True the background is estimated and subtracted +SPECTRACTOR_BACKGROUND_SUBTRACTION = True # if True, the background is estimated and subtracted SPECTRACTOR_FIT_TIMEOUT_PER_ITER = 600 # maximum time per gradient descent iteration before TimeoutError in seconds SPECTRACTOR_FIT_TIMEOUT = 3600 # maximum time per gradient descent before TimeoutError in seconds SPECTRACTOR_ATMOSPHERE_SIM = "none" # library to compute atmospheric transmission: none, libradtran, getobsatmo @@ -74,7 +74,7 @@ def __getattr__(name): OBS_SURFACE = 6361 # Effective surface of the telescope in cm**2 accounting for obscuration OBS_EPOCH = "J2000.0" OBS_OBJECT_TYPE = 'STAR' # To choose between STAR, HG-AR, MONOCHROMATOR -OBS_FULL_INSTRUMENT_TRANSMISSON = 'ctio_throughput_300517_v1.txt' # Full instrument transmission file +OBS_FULL_INSTRUMENT_TRANSMISSION = 'ctio_throughput_300517_v1.txt' # Full instrument transmission file OBS_TRANSMISSION_SYSTEMATICS = 0.005 OBS_CAMERA_ROTATION = 0 # Camera (x,y) rotation angle with respect to (north-up, east-left) system in degrees OBS_CAMERA_DEC_FLIP_SIGN = 1 # Camera (x,y) flip signs with respect to (north-up, east-left) system @@ -121,7 +121,7 @@ def __getattr__(name): PSF_POLY_TYPE = "polynomial" # polynomial type: must be polynomial or legendre # Detection line algorithm -CALIB_BGD_ORDER = 3 # order of the background polynome to fit +CALIB_BGD_ORDER = 3 # order of the background polynomial to fit CALIB_PEAK_WIDTH = 7 # half range to look for local extrema in pixels around tabulated line values CALIB_BGD_WIDTH = 10 # size of the peak sides to use to fit spectrum base line CALIB_SAVGOL_WINDOW = 5 # window size for the savgol filter in pixels diff --git a/spectractor/simulation/adr.py b/spectractor/simulation/adr.py index 7bac14ce6..593787a84 100644 --- a/spectractor/simulation/adr.py +++ b/spectractor/simulation/adr.py @@ -407,8 +407,8 @@ def rec2pol(x, y, deg=False): Functions to: - get adr object from fits file - get list of shift due to adr corresponding to a list of lambdas - - get maximum shift due to adr between (in th range of lambdas given) - - plot the adr along both axis and the biggest one against the wavelength + - get maximum shift due to adr in the given wavelength range + - plot the adr along both axes and the largest one against wavelength Example to get shift in pixels: instanciation_adr(fitsfile, latitude, lbda_ref) xs, ys = get_adr_shift_for_lbdas(adr_object, lbdas) @@ -427,7 +427,7 @@ def adr_calib(lambdas, params, lat, lambda_ref=550): elif isinstance(lat, AC.Latitude): lat = lat else: - raise TypeError('Latitude type is neither a str, float nor an astropy.coordinates') + raise TypeError('Latitude type is neither a str, float, nor an astropy.coordinates.Latitude') meadr = instanciation_adr(params, lat, lambda_ref * 10) @@ -450,7 +450,7 @@ def instanciation_adr(params, latitude, lbda_ref): dec = dec hour_angle = hour_angle else: - raise TypeError('dec/hour_angle type is neither a str nor an astropy.coordinates') + raise TypeError('dec/hour_angle type is neither a str nor an astropy.coordinates.Angle') temperature, pressure, humidity, airmass = params[2:] diff --git a/spectractor/simulation/atmosphere.py b/spectractor/simulation/atmosphere.py index 10746a175..9b8bbbec0 100644 --- a/spectractor/simulation/atmosphere.py +++ b/spectractor/simulation/atmosphere.py @@ -70,7 +70,7 @@ def __init__(self, airmass, pressure, temperature, lambda_min=250, lambda_max=12 self.lambda_min = self.emulator.WLMIN self.lambda_max = self.emulator.WLMAX elif parameters.SPECTRACTOR_ATMOSPHERE_SIM.lower() == "none": - raise ValueError(f"Can not compute atmospheric transmission with {parameters.SPECTRACTOR_ATMOSPHERE_SIM=}. " + raise ValueError(f"Cannot compute atmospheric transmission with {parameters.SPECTRACTOR_ATMOSPHERE_SIM=}. " f"Check your configuration.") elif parameters.SPECTRACTOR_ATMOSPHERE_SIM.lower() == "libradtran": self.emulator = None @@ -239,9 +239,9 @@ def __init__(self, image_filename="", spectrum_filename="", atmgrid_filename="", Parameters ---------- image_filename: str, optional - The original image fits file name from which the grid was computed or has to be computed (default: ""). + The original image FITS file name from which the grid was computed or has to be computed (default: ""). spectrum_filename: str, optional - The file name of the spectrum fits file name from which the grid was computed or has to be computed (default: ""). + The file name of the spectrum FITS file name from which the grid was computed or has to be computed (default: ""). atmgrid_filename: str, optional The file name of the atmospheric grid if it exists (default: ""). airmass: float, optional @@ -307,7 +307,7 @@ def __init__(self, image_filename="", spectrum_filename="", atmgrid_filename="", def set_grid(self, pwv_grid=[0, 10, 10], ozone_grid=[100, 700, 7], aerosol_grid=[0, 0.1, 10], lambdas=parameters.LAMBDAS): - """Set the size of the simulation grid self.atmgrid before compute it. + """Set the size of the simulation grid self.atmgrid before computing it. The first column of self.atmgrid will contain the wavelengths set by lambdas argument, the other columns the future simulations. diff --git a/spectractor/simulation/throughput.py b/spectractor/simulation/throughput.py index 40a54b33e..6c7d506d6 100644 --- a/spectractor/simulation/throughput.py +++ b/spectractor/simulation/throughput.py @@ -94,7 +94,7 @@ def plot_transmission_simple(ax, lambdas, transmissions, uncertainties=None, la >>> plot_transmission_simple(ax, lambdas, transmissions, errors, title="CTIO", label="FGB37") >>> lambdas, transmissions, errors = load_transmission_file(os.path.join(parameters.THROUGHPUT_DIR, "RG715.txt")) >>> plot_transmission_simple(ax, lambdas, transmissions, errors, title="CTIO", label="RG715") - >>> lambdas, transmissions, errors = load_transmission_file(os.path.join(parameters.THROUGHPUT_DIR, parameters.OBS_FULL_INSTRUMENT_TRANSMISSON)) + >>> lambdas, transmissions, errors = load_transmission_file(os.path.join(parameters.THROUGHPUT_DIR, parameters.OBS_FULL_INSTRUMENT_TRANSMISSION)) >>> plot_transmission_simple(ax, lambdas, transmissions, errors, title="CTIO", label="Full instrument") >>> if parameters.DISPLAY: plt.show() @@ -167,7 +167,7 @@ def load_transmission(self): """ wl, trm, err = load_transmission_file(os.path.join(parameters.THROUGHPUT_DIR, - parameters.OBS_FULL_INSTRUMENT_TRANSMISSON)) + parameters.OBS_FULL_INSTRUMENT_TRANSMISSION)) to = interp1d(wl, trm, kind='linear', bounds_error=False, fill_value=0.) err = np.sqrt(err ** 2 + parameters.OBS_TRANSMISSION_SYSTEMATICS ** 2) to_err = interp1d(wl, err, kind='linear', bounds_error=False, fill_value=0.) diff --git a/spectractor/tools.py b/spectractor/tools.py index f585d4874..8c9459ca3 100644 --- a/spectractor/tools.py +++ b/spectractor/tools.py @@ -130,7 +130,7 @@ def line(x, a, b): # noinspection PyTypeChecker def fit_gauss(x, y, guess=[10, 1000, 1], bounds=(-np.inf, np.inf), sigma=None): """Fit a Gaussian profile to data, using curve_fit. The mean guess value of the Gaussian - must not be far from the truth values. Boundaries helps a lot also. + must not be far from the truth values. Boundaries help a lot also. Parameters ---------- @@ -213,7 +213,7 @@ def multigauss_and_line(x, *params): def fit_multigauss_and_line(x, y, guess=[0, 1, 10, 1000, 1, 0], bounds=(-np.inf, np.inf)): """Fit a multiple Gaussian profile plus a straight line to data, using curve_fit. The mean guess value of the Gaussian must not be far from the truth values. - Boundaries helps a lot also. The order of the parameters is line slope, line intercept, + Boundaries help a lot also. The order of the parameters is line slope, line intercept, and then block of 3 parameters for the Gaussian profiles like amplitude, mean and standard deviation. @@ -322,7 +322,7 @@ def multigauss_and_bgd(x, *params): """Multiple Gaussian profile plus a polynomial background to data. Polynomial function is based on the orthogonal Legendre polynomial basis. The degree of the polynomial background is fixed by parameters.CALIB_BGD_NPARAMS. - The order of the parameters is a first block CALIB_BGD_NPARAMS parameters (from low to high Legendre polynome degree, + The order of the parameters is a first block CALIB_BGD_NPARAMS parameters (from low to high Legendre polynomial degree, contrary to np.polyval), and then block of 3 parameters for the Gaussian profiles like amplitude, mean and standard deviation. @@ -379,7 +379,7 @@ def multigauss_and_bgd(x, *params): def multigauss_and_bgd_jacobian(x, *params): """Jacobien of the multiple Gaussian profile plus a polynomial background to data. The degree of the polynomial background is fixed by parameters.CALIB_BGD_NPARAMS. - The order of the parameters is a first block CALIB_BGD_NPARAMS parameters (from low to high Legendre polynome degree, + The order of the parameters is a first block CALIB_BGD_NPARAMS parameters (from low to high Legendre polynomial degree, contrary to np.polyval), and then block of 3 parameters for the Gaussian profiles like amplitude, mean and standard deviation. x values are renormalised on the [-1, 1] interval for the background. @@ -425,7 +425,7 @@ def multigauss_and_bgd_jacobian(x, *params): def fit_multigauss_and_bgd(x, y, guess=[0, 1, 10, 1000, 1, 0], bounds=(-np.inf, np.inf), sigma=None): """Fit a multiple Gaussian profile plus a polynomial background to data, using iminuit. The mean guess value of the Gaussian must not be far from the truth values. - Boundaries helps a lot also. The degree of the polynomial background is fixed by parameters.CALIB_BGD_NPARAMS. + Boundaries help a lot also. The degree of the polynomial background is fixed by parameters.CALIB_BGD_NPARAMS. The order of the parameters is a first block CALIB_BGD_NPARAMS parameters (from high to low monomial terms, same as np.polyval), and then block of 3 parameters for the Gaussian profiles like amplitude, mean and standard deviation. x values are renormalised on the [-1, 1] interval for the background. @@ -1215,13 +1215,13 @@ def compute_fwhm(x, y, minimum=0, center=None, full_output=False, epsilon=1e-3): Parameters ---------- x: array_like - The abscisse array. + The abscissa array. y: array_like The function array. minimum: float, optional - The minimum reference from which to compyte half the height (default: 0). + The minimum reference from which to compute half the height (default: 0). center: float, optional - The center of the curve. If None, the weighted averageof the y(x) distribution is computed (default: None). + The center of the curve. If None, the weighted average of the y(x) distribution is computed (default: None). full_output: bool, optional If True, half maximum, the edges of the curve and the curve center are given in output (default: False). epsilon: float, optional @@ -1340,17 +1340,17 @@ def eq(xx): def compute_integral(x, y, bounds=None): """ - Compute the integral of an y(x) curve. The curve is interpolated and extrapolated with cubic splines. + Compute the integral of a y(x) curve. The curve is interpolated and extrapolated with cubic splines. If not provided, bounds are set to the x array edges. Parameters ---------- x: array_like - The abscisse array. + The abscissa array. y: array_like The function array. bounds: array_like, optional - The bounds of the integral. If None, the edges of thex array are taken (default bounds=None). + The bounds of the integral. If None, the edges of the x array are taken (default bounds=None). Returns ------- @@ -1795,7 +1795,7 @@ def detect_peaks(image): background = (image == 0) # a little technicality: we must erode the background in order to - # successfully subtract it form local_max, otherwise a line will + # successfully subtract it from local_max, otherwise a line will # appear along the background border (artifact of the local maximum filter) eroded_background = binary_erosion(background, structure=neighborhood, border_value=50) @@ -1936,7 +1936,7 @@ def plot_spectrum_simple(ax, lambdas, data, data_err=None, xlim=None, color='r', data_err: array, optional The spectrum uncertainty array (default: None). xlim: list, optional - List of minimum and maximum abscisses (default: None). + List containing a minimum and a maximum x value (default: None). color: str, optional String for the color of the spectrum (default: 'r'). linestyle: str, optional @@ -2608,16 +2608,16 @@ def compute_correlation_matrix(cov): def _convert_latex_to_plain_text(text): - """Convert LaTeX math symbols to plain text for matplotlib compatibility. - + r"""Convert LaTeX math symbols to plain text for matplotlib compatibility. + Remove math mode delimiters ($) and replace LaTeX Greek letters with their plain text names to avoid mathtext parsing errors in matplotlib versions with limited LaTeX support. - + Parameters ---------- text : str Text potentially containing LaTeX math symbols (e.g., r"$\gamma$", "$y_c^{(0)}$"). - + Returns ------- str @@ -2649,17 +2649,17 @@ def _convert_latex_to_plain_text(text): r'\psi': 'psi', r'\omega': 'omega', } - + # Remove $ delimiters plain_text = text.replace('$', '') - + # Replace LaTeX commands with plain text for latex, plain in latex_to_plain.items(): plain_text = plain_text.replace(latex, plain) - + # Replace remaining LaTeX syntax for readability plain_text = plain_text.replace('{', '').replace('}', '') - + return plain_text @@ -2703,7 +2703,7 @@ def flip_and_rotate_radec_vector_to_xy_vector(ra, dec, camera_angle=0, flip_ra_s Vector coordinates along DEC direction. camera_angle: float Angle of the camera between y axis and the North Celestial Pole counterclockwise, or equivalently between - the x axis and the West direction counterclokwise. Units are degrees. (default: 0). + the x axis and the West direction counterclockwise. Units are degrees. (default: 0). flip_ra_sign: -1, 1, optional Flip RA axis is value is -1 (default: 1). flip_dec_sign: -1, 1, optional