def test_reduced_arcs_contains_stable_wavelength_solution(config): """ Make sure that the wavelength solution gives same results on different runs. """ output = os.path.join(config.output_dir, config.filename) reference = os.path.join(config.ref_dir, config.filename) if not os.path.exists(output): pytest.skip("Output file not found: {}".format(output)) if not os.path.exists(reference): pytest.fail("Reference file not found: {}".format(reference)) ad_out = config.ad ad_ref = astrodata.open(reference) for ext_out, ext_ref in zip(ad_out, ad_ref): model = am.get_named_submodel(ext_out.wcs.forward_transform, 'WAVE') ref_model = am.get_named_submodel(ext_ref.wcs.forward_transform, 'WAVE') x = np.arange(ext_out.shape[1]) y = model(x) ref_y = ref_model(x) np.testing.assert_allclose(y, ref_y, atol=1) del ad_out, ad_ref
def test_regression_determine_wavelength_solution(ad, fwidth, order, min_snr, caplog, change_working_dir, ref_ad_factory, request): """ Make sure that the wavelength solution gives same results on different runs. """ caplog.set_level(logging.INFO, logger="geminidr") with change_working_dir(): logutils.config( file_name='log_regress_{:s}.txt'.format(ad.data_label())) p = GMOSLongslit([ad]) p.viewer = geminidr.dormantViewer(p, None) p.determineWavelengthSolution( order=order, min_snr=min_snr, fwidth=fwidth, **determine_wavelength_solution_parameters) wcalibrated_ad = p.writeOutputs().pop() for record in caplog.records: if record.levelname == "WARNING": assert "No acceptable wavelength solution found" not in record.message ref_ad = ref_ad_factory(wcalibrated_ad.filename) model = am.get_named_submodel(wcalibrated_ad[0].wcs.forward_transform, "WAVE") ref_model = am.get_named_submodel(ref_ad[0].wcs.forward_transform, "WAVE") x = np.arange(wcalibrated_ad[0].shape[1]) wavelength = model(x) ref_wavelength = ref_model(x) pixel_scale = wcalibrated_ad[0].pixel_scale() # arcsec / px slit_size_in_arcsec = float(wcalibrated_ad[0].focal_plane_mask().replace( 'arcsec', '')) slit_size_in_px = slit_size_in_arcsec / pixel_scale dispersion = abs( wcalibrated_ad[0].dispersion(asNanometers=True)) # nm / px tolerance = 0.5 * (slit_size_in_px * dispersion) np.testing.assert_allclose(wavelength, ref_wavelength, rtol=tolerance) if request.config.getoption("--do-plots"): do_plots(wcalibrated_ad)
def wavelength_calibration_plots(self): """ Makes the Wavelength Calibration Diagnosis Plots for each extension inside the reduced arc. """ for ext_num, ext in enumerate(self.ad): if not hasattr(ext, "WAVECAL"): continue peaks = ext.WAVECAL[ "peaks"] - 1 # ToDo: Refactor peaks to be 0-indexed wavelengths = ext.WAVECAL["wavelengths"] wavecal_model = am.get_named_submodel(ext.wcs.forward_transform, "WAVE") middle = ext.data.shape[0] // 2 sum_size = 10 r1 = middle - sum_size // 2 r2 = middle + sum_size // 2 mask = np.round(np.average(ext.mask[r1:r2], axis=0)).astype(int) data = np.ma.masked_where(mask > 0, np.sum(ext.data[r1:r2], axis=0)) data = (data - data.min()) / data.ptp() self.plot_lines(ext_num, data, peaks, wavecal_model) self.plot_non_linear_components(ext_num, peaks, wavelengths, wavecal_model) self.plot_wavelength_solution_residuals(ext_num, peaks, wavelengths, wavecal_model) self.create_artifact_from_plots()
def test_consistent_air_and_vacuum_solutions(ad, fwidth, order, min_snr): p = GMOSLongslit([]) p.viewer = geminidr.dormantViewer(p, None) ad_air = p.determineWavelengthSolution( [deepcopy(ad)], order=order, min_snr=min_snr, fwidth=fwidth, in_vacuo=False, **determine_wavelength_solution_parameters).pop() ad_vac = p.determineWavelengthSolution( [ad], order=order, min_snr=min_snr, fwidth=fwidth, in_vacuo=True, **determine_wavelength_solution_parameters).pop() wave_air = am.get_named_submodel(ad_air[0].wcs.forward_transform, "WAVE") wave_vac = am.get_named_submodel(ad_vac[0].wcs.forward_transform, "WAVE") x = np.arange(ad_air[0].shape[1]) wair = wave_air(x) wvac = air_to_vac(wair * u.nm).to(u.nm).value dw = wvac - wave_vac(x) assert abs(dw).max() < 0.001
def assert_wavelength_solutions_are_close(ad, ad_ref, atol=0, rtol=1e-7): """ Checks if two :class:`~astrodata.AstroData` (or any subclass) have the wavelength solution. Parameters ---------- ad : :class:`astrodata.AstroData` or any subclass AstroData object to be checked. ad_ref : :class:`astrodata.AstroData` or any subclass AstroData object used as reference atol, rtol : float absolute and relative tolerances """ for ext, ext_ref in zip(ad, ad_ref): wcal = get_named_submodel(ext.wcs.forward_transform, 'WAVE') wcal_ref = get_named_submodel(ext_ref.wcs.forward_transform, 'WAVE') assert_allclose(wcal.parameters, wcal_ref.parameters, atol=atol, rtol=rtol)
def do_plots(ad): """ Generate diagnostic plots. Parameters ---------- ad : astrodata """ output_dir = ("./plots/geminidr/gmos/" "test_gmos_spect_ls_determine_wavelength_solution") os.makedirs(output_dir, exist_ok=True) name, _ = os.path.splitext(ad.filename) grating = ad.disperser(pretty=True) bin_x = ad.detector_x_bin() bin_y = ad.detector_y_bin() central_wavelength = ad.central_wavelength(asNanometers=True) p = GMOSLongslit([ad]) arc_table = os.path.join(p.inst_lookups, "CuAr_GMOS.dat") arc_lines = np.loadtxt(arc_table, usecols=[0]) / 10.0 for ext_num, ext in enumerate(ad): if not hasattr(ext, "WAVECAL"): continue peaks = ext.WAVECAL["peaks"] - 1 # ToDo: Refactor peaks to be 0-indexed wavelengths = ext.WAVECAL["wavelengths"] wavecal_model = am.get_named_submodel(ext.wcs.forward_transform, "WAVE") middle = ext.data.shape[0] // 2 sum_size = 10 r1 = middle - sum_size // 2 r2 = middle + sum_size // 2 mask = np.round(np.average(ext.mask[r1:r2], axis=0)).astype(int) data = np.ma.masked_where(mask > 0, np.sum(ext.data[r1:r2], axis=0)) data = (data - data.min()) / data.ptp() # -- Plot lines -- fig, ax = plt.subplots( dpi=150, num="{:s}_{:d}_{:s}_{:.0f}".format( name, ext_num, grating, central_wavelength)) w = wavecal_model(np.arange(data.size)) arcs = [ax.vlines(line, 0, 1, color="k", alpha=0.25) for line in arc_lines] wavs = [ax.vlines(peak, 0, 1, color="r", ls="--", alpha=0.25) for peak in wavecal_model(peaks)] plot, = ax.plot(w, data, "k-", lw=0.75) ax.legend((plot, arcs[0], wavs[0]), ("Normalized Data", "Reference Lines", "Matched Lines")) x0, x1 = wavecal_model([0, data.size]) ax.grid(alpha=0.1) ax.set_xlim(x0, x1) ax.set_xlabel("Wavelength [nm]") ax.set_ylabel("Normalized intensity") ax.set_title("Wavelength Calibrated Spectrum for\n" "{:s}\n obtained with {:s} at {:.0f} nm".format( name, grating, central_wavelength)) if x0 > x1: ax.invert_xaxis() fig_name = os.path.join(output_dir, "{:s}_{:d}_{:s}_{:.0f}.png".format( name, ext_num, grating, central_wavelength)) fig.savefig(fig_name) del fig, ax # -- Plot non-linear components --- fig, ax = plt.subplots( dpi=150, num="{:s}_{:d}_{:s}_{:.0f}_non_linear_comps".format( name, ext_num, grating, central_wavelength)) non_linear_model = wavecal_model.copy() _ = [setattr(non_linear_model, "c{}".format(k), 0) for k in [0, 1]] residuals = wavelengths - wavecal_model(peaks) p = np.linspace(min(peaks), max(peaks), 1000) ax.plot(wavecal_model(p), non_linear_model(p), "C0-", label="Generic Representation") ax.plot(wavecal_model(peaks), non_linear_model(peaks) + residuals, "ko", label="Non linear components and residuals") ax.legend() ax.grid(alpha=0.25) ax.set_xlabel("Wavelength [nm]") ax.set_title("Non-linear components for\n" "{:s} obtained with {:s} at {:.0f}".format( name, grating, central_wavelength)) fig_name = os.path.join( output_dir, "{:s}_{:d}_{:s}_{:.0f}_non_linear_comps.png".format( name, ext_num, grating, central_wavelength)) fig.savefig(fig_name) del fig, ax # -- Plot Wavelength Solution Residuals --- fig, ax = plt.subplots( dpi=150, num="{:s}_{:d}_{:s}_{:.0f}_residuals".format( name, ext_num, grating, central_wavelength)) ax.plot(wavelengths, wavelengths - wavecal_model(peaks), "ko") ax.grid(alpha=0.25) ax.set_xlabel("Wavelength [nm]") ax.set_ylabel("Residuum [nm]") ax.set_title("Wavelength Calibrated Residuum for\n" "{:s} obtained with {:s} at {:.0f}".format( name, grating, central_wavelength)) fig_name = os.path.join( output_dir, "{:s}_{:d}_{:s}_{:.0f}_residuals.png".format( name, ext_num, grating, central_wavelength)) fig.savefig(fig_name) # -- Create artifacts --- if "BUILD_ID" in os.environ: branch_name = os.environ["BRANCH_NAME"].replace("/", "_") build_number = int(os.environ["BUILD_NUMBER"]) tar_name = os.path.join(output_dir, "plots_{:s}_b{:03d}.tar.gz".format( branch_name, build_number)) with tarfile.open(tar_name, "w:gz") as tar: for _file in glob.glob(os.path.join(output_dir, "*.png")): tar.add(name=_file, arcname=os.path.basename(_file)) target_dir = "./plots/" target_file = os.path.join(target_dir, os.path.basename(tar_name)) os.makedirs(target_dir, exist_ok=True) os.rename(tar_name, target_file)