def photometry_to_table( self, output: str = None, best: bool = False, fmts: List[str] = ("ascii.ecsv", "ascii.csv") ): """ Converts the photometry information, which is stored internally as a dictionary, into an astropy QTable. :param output: Where to write table. :return: """ if output is None: output = self.build_photometry_table_path() # if self.photometry_tbl is None: tbls = [] for instrument_name in self.photometry: instrument = inst.Instrument.from_params(instrument_name) for filter_name in self.photometry[instrument_name]: fil = instrument.filters[filter_name] if best: phot_dict, _ = self.select_photometry_sep(fil=filter_name, instrument=instrument_name) phot_dict["band"] = filter_name phot_dict["instrument"] = instrument_name phot_dict["lambda_eff"] = u.check_quantity( number=fil.lambda_eff, unit=units.Angstrom ) # tbl = table.QTable([phot_dict]) tbls.append(phot_dict) print("phot_dict:") print(phot_dict) else: for epoch in self.photometry[instrument_name][filter_name]: phot_dict = self.photometry[instrument_name][filter_name][epoch].copy() phot_dict["band"] = filter_name phot_dict["instrument"] = instrument_name phot_dict["lambda_eff"] = u.check_quantity( number=fil.lambda_eff, unit=units.Angstrom ) # tbl = table.QTable([phot_dict]) tbls.append(phot_dict) print(tbls) if best: self.photometry_tbl = table.vstack(tbls) else: self.photometry_tbl = table.QTable(tbls) if output is not False: for fmt in fmts: self.photometry_tbl.write(output.replace(".ecsv", fmt[fmt.find("."):]), format=fmt, overwrite=True) return self.photometry_tbl
def solve_field( image_files: Union[str, list], base_filename: str = "astrometry", overwrite: bool = True, tweak: bool = True, search_radius: units.Quantity = 1 * units.degree, centre: SkyCoord = None, guess_scale: bool = True, time_limit: units.Quantity = None, verify: bool = True, odds_to_tune_up: float = 1e6, odds_to_solve: float = 1e9, *flags, **params): """ Returns True if successful (by checking whether the corrected file is generated); False if not. :param image_files: :param base_filename: :param overwrite: :param flags: :param params: :return: """ params["o"] = base_filename params["odds-to-tune-up"] = odds_to_tune_up params["odds-to-solve"] = odds_to_solve if time_limit is not None: params["l"] = check_quantity(time_limit, units.second).value if search_radius is not None: params["radius"] = search_radius.to(units.deg).value if centre is not None: params["ra"] = centre.ra.to(units.deg).value params["dec"] = centre.dec.to(units.deg).value debug_print(1, "solve_field(): tweak ==", tweak) flags = list(flags) if overwrite: flags.append("O") if guess_scale: flags.append("g") if not tweak: flags.append("T") if not verify: flags.append("y") system_command("solve-field", image_files, False, True, False, *flags, **params) if isinstance(image_files, list): image_path = image_files[0] else: image_path = image_files check_dir = os.path.split(image_path)[0] check_path = os.path.join(check_dir, f"{base_filename}.new") print(f"Checking for result file at {check_path}...") return os.path.isfile(check_path)
def test_check_quantity(): number = 10. assert u.check_quantity(number=number, unit=units.meter) == number * units.meter number = 10. * units.meter assert u.check_quantity(number=number, unit=units.meter) == number number = 1000. * units.centimeter assert u.check_quantity(number=number, unit=units.meter, convert=True) == 10. * units.meter assert u.check_quantity(number=number, unit=units.meter, convert=True).unit == units.meter assert u.check_quantity(number=number, unit=units.meter, convert=False).unit == units.centimeter with pytest.raises(units.UnitsError) as e: u.check_quantity(number=number, unit=units.meter, allow_mismatch=False) assert e.type is units.UnitsError with pytest.raises(units.UnitsError) as e: u.check_quantity(number=number, unit=units.joule) assert e.type is units.UnitsError
def __init__( self, name: str = None, position: Union[SkyCoord, str] = None, position_err: Union[float, units.Quantity, dict, PositionUncertainty, tuple] = 0.0 * units.arcsec, field=None, row: table.Row = None, plotting: dict = None ): self.name = name self.cat_row = row self.position = None self.position_err = None if self.cat_row is not None: self.position_from_cat_row() else: self.position = astm.attempt_skycoord(position) if type(position_err) is not PositionUncertainty: self.position_err = PositionUncertainty(uncertainty=position_err, position=self.position) self.position_galactic = None if isinstance(self.position, SkyCoord): self.position_galactic = self.position.transform_to("galactic") if self.name is None: self.jname() self.name_filesys = None self.set_name_filesys() self.photometry = {} self.photometry_tbl = None self.data_path = None self.output_file = None self.field = field self.irsa_extinction_path = None self.irsa_extinction = None self.ebv_sandf = None self.extinction_power_law = None self.paths = {} if self.data_path is not None: self.load_output_file() if isinstance(plotting, dict): self.plotting_params = plotting if "frame" in self.plotting_params and self.plotting_params["frame"] is not None: self.plotting_params["frame"] = u.check_quantity(self.plotting_params["frame"], units.arcsec) else: self.plotting_params = {} self.a = None self.b = None self.theta = None self.kron = None
def source_extractor( image_path: str, output_dir: str = None, configuration_file: str = None, parameters_file: str = None, catalog_name: str = None, copy_params: bool = True, template_image_path: str = None, **configs): """ :param configs: Any source-extractor (sextractor) parameter, normally read via the config file but that can be overridden by passing to the shell command, can be given here. """ if "gain" in configs: configs["gain"] = u.check_quantity(number=configs["gain"], unit=gain_unit).to(gain_unit).value old_dir = os.getcwd() if output_dir is None: output_dir = os.getcwd() else: u.mkdir_check(output_dir) os.chdir(output_dir) if copy_params: os.system(f"cp {os.path.join(p.path_to_config_psfex(), '*')} .") sys_str = "source-extractor " if template_image_path is not None: sys_str += f"{template_image_path}," sys_str += image_path + " " if configuration_file is not None: sys_str += f" -c {configuration_file}" if catalog_name is None: image_name = os.path.split(image_path)[-1] catalog_name = f"{image_name}.cat" sys_str += f" -CATALOG_NAME {catalog_name}" if parameters_file is not None: sys_str += f" -PARAMETERS_NAME {parameters_file}" for param in configs: sys_str += f" -{param.upper()} {configs[param]}" u.system_command_verbose(sys_str, error_on_exit_code=True) catalog_path = os.path.join(output_dir, catalog_name) os.chdir(old_dir) return catalog_path
def __init__( self, name: str = None, position: Union[SkyCoord, str] = None, position_err: Union[float, units.Quantity, dict, PositionUncertainty, tuple] = 0.0 * units.arcsec, host_galaxy: Galaxy = None, dm: Union[float, units.Quantity] = None, field=None, plotting: dict = None, ): super().__init__( name=name, position=position, position_err=position_err, field=field, plotting=plotting ) self.host_galaxy = host_galaxy self.dm = dm if self.dm is not None: self.dm = u.check_quantity(self.dm, unit=dm_units)
def ingest_filter_transmission(path: str, fil_name: str, instrument: str, instrument_response: bool = False, atmosphere: bool = False, lambda_eff: units.Quantity = None, fwhm: float = None, source: str = None, wavelength_unit: units.Unit = units.Angstrom, percentage: bool = False, quiet: bool = False): """ :param path: :param fil_name: :param instrument: :param instrument_response: Filter curve includes instrument response :param atmosphere: Filter curve includes atmospheric transmission :param lambda_eff: :param fwhm: :param source: :param wavelength_unit: :param percentage: :param quiet: :return: """ if not wavelength_unit.is_equivalent(units.Angstrom): raise units.UnitTypeError( f"Wavelength units must be of type length, not {wavelength_unit}") type_str = "_filter" if instrument_response: type_str += "_instrument" if atmosphere: type_str += "_atmosphere" params = filter_params(f=fil_name, instrument=instrument, quiet=quiet) if params is None: params = new_filter_params(quiet=quiet) params['name'] = fil_name params['instrument'] = instrument if lambda_eff is not None: lambda_eff = u.check_quantity(lambda_eff, unit=units.Angstrom, convert=True) params['lambda_eff'] = lambda_eff # TODO: If None, measure? if fwhm is not None: params['fwhm'] = fwhm # TODO: If None, measure? if source is not None: params[f'source{type_str}'] = source tbl = QTable.read(path, format="ascii") tbl["col1"].name = "wavelength" tbl["wavelength"] *= wavelength_unit tbl["wavelength"] = tbl["wavelength"].to("Angstrom") tbl["col2"].name = "transmission" if percentage: tbl["transmission"] /= 100 tbl.sort("wavelength") params[f'wavelengths{type_str}'] = tbl["wavelength"].value.tolist() params[f'transmissions{type_str}'] = tbl["transmission"].value.tolist() save_params(file=os.path.join(param_dir, 'filters', f'{instrument}-{fil_name}'), dictionary=params, quiet=quiet)
def __init__( self, uncertainty: Union[float, units.Quantity, dict, tuple] = None, position: SkyCoord = None, ra_err_sys: Union[float, units.Quantity] = None, ra_err_stat: Union[float, units.Quantity] = None, dec_err_sys: Union[float, units.Quantity] = None, dec_err_stat: Union[float, units.Quantity] = None, a_stat: Union[float, units.Quantity] = None, a_sys: Union[float, units.Quantity] = None, b_stat: Union[float, units.Quantity] = None, b_sys: Union[float, units.Quantity] = None, theta: Union[float, units.Quantity] = None, sigma: float = None ): """ If a single value is provided for uncertainty, the uncertainty ellipse will be assumed to be circular. Values in dictionary, if provided, override values given as arguments. Position values provided without units are assumed to be in degrees. On the other hand, uncertainty values provided without units are assumed to be in arcseconds; except for uncertainties in RA, which are assumed in RA seconds. :param uncertainty: :param position: :param ra_err_sys: :param ra_err_stat: :param dec_err_sys: :param dec_err_stat: :param a_stat: :param a_sys: :param b_stat: :param b_sys: :param theta: :param sigma: The confidence interval (expressed in multiples of sigma) of the uncertainty ellipse. """ self.sigma = sigma # Assign values from dictionary, if provided. if type(uncertainty) is dict: if "ra" in uncertainty and uncertainty["ra"] is not None: if "sys" in uncertainty["ra"] and uncertainty["ra"]["sys"] is not None: ra_err_sys = uncertainty["ra"]["sys"] if "stat" in uncertainty["ra"] and uncertainty["ra"]["stat"] is not None: ra_err_stat = uncertainty["ra"]["stat"] if "dec" in uncertainty and uncertainty["dec"] is not None: if "sys" in uncertainty["dec"] and uncertainty["dec"]["sys"] is not None: dec_err_sys = uncertainty["dec"]["sys"] if "stat" in uncertainty["dec"] and uncertainty["dec"]["stat"] is not None: dec_err_stat = uncertainty["dec"]["stat"] if "a" in uncertainty and uncertainty["a"] is not None: if "sys" in uncertainty["a"] and uncertainty["a"]["sys"] is not None: a_sys = uncertainty["a"]["sys"] if "stat" in uncertainty["a"] and uncertainty["a"]["stat"] is not None: a_stat = uncertainty["a"]["stat"] if "b" in uncertainty and uncertainty["b"] is not None: if "sys" in uncertainty["b"] and uncertainty["b"]["sys"] is not None: b_sys = uncertainty["b"]["sys"] if "stat" in uncertainty["b"] and uncertainty["a"]["stat"] is not None: b_stat = uncertainty["b"]["stat"] if "theta" in uncertainty and uncertainty["theta"] is not None: theta = uncertainty["theta"] # If uncertainty is a single value, assume a circular uncertainty region without distinction between systematic # and statistical. elif uncertainty is not None: a_stat = uncertainty a_sys = 0.0 b_stat = uncertainty b_sys = 0.0 theta = 0.0 # Check whether we're specifying uncertainty using equatorial coordinates or ellipse parameters. u.debug_print(2, "PositionUncertainty.__init__(): a_stat, a_sys, b_stat, b_sys, theta, position ==", a_stat, a_sys, b_stat, b_sys, theta, position) u.debug_print(2, "PositionUncertainty.__init__(): ra_err_sys, ra_err_stat, dec_err_sys, dec_err_stat ==", ra_err_sys, ra_err_stat, dec_err_sys, dec_err_stat) if a_stat is not None and a_sys is not None and b_stat is not None and b_sys is not None and theta is not None and position is not None: ellipse = True elif ra_err_sys is not None and ra_err_stat is not None and dec_err_sys is not None and dec_err_stat is not None: ellipse = False else: raise ValueError( "Either all ellipse values (a, b, theta) or all equatorial values (ra, dec, position) must be provided.") ra_err_sys = u.check_quantity(number=ra_err_sys, unit=units.hourangle / 3600) ra_err_stat = u.check_quantity(number=ra_err_stat, unit=units.hourangle / 3600) dec_err_sys = u.check_quantity(number=dec_err_sys, unit=units.arcsec) dec_err_stat = u.check_quantity(number=dec_err_stat, unit=units.arcsec) # Convert equatorial uncertainty to ellipse with theta=0 if not ellipse: ra = position.ra dec = position.dec a_sys = SkyCoord(0.0 * units.degree, dec).separation(SkyCoord(ra_err_sys, dec)) a_stat = SkyCoord(0.0 * units.degree, dec).separation(SkyCoord(ra_err_stat, dec)) b_sys = SkyCoord(ra, dec).separation(SkyCoord(ra, dec + dec_err_sys)) b_stat = SkyCoord(ra, dec).separation(SkyCoord(ra, dec + dec_err_stat)) a_sys, b_sys = max(a_sys, b_sys), min(a_sys, b_sys) a_stat, b_stat = max(a_stat, b_stat), min(a_stat, b_stat) theta = 0.0 * units.degree # Or use ellipse parameters as given. else: a_sys = u.check_quantity(number=a_sys, unit=units.arcsec) a_stat = u.check_quantity(number=a_stat, unit=units.arcsec) b_sys = u.check_quantity(number=b_sys, unit=units.arcsec) b_stat = u.check_quantity(number=b_stat, unit=units.arcsec) theta = u.check_quantity(number=theta, unit=units.arcsec) self.a_sys = a_sys self.a_stat = a_stat self.b_sys = b_sys self.b_stat = b_stat self.theta = theta self.ra_sys = ra_err_sys self.dec_sys = dec_err_sys self.ra_stat = ra_err_stat self.dec_stat = dec_err_stat
def add_photometry( self, instrument: Union[str, inst.Instrument], fil: Union[str, inst.Filter], epoch_name: str, mag: units.Quantity, mag_err: units.Quantity, snr: float, ellipse_a: units.Quantity, ellipse_a_err: units.Quantity, ellipse_b: units.Quantity, ellipse_b_err: units.Quantity, ellipse_theta: units.Quantity, ellipse_theta_err: units.Quantity, ra: units.Quantity, ra_err: units.Quantity, dec: units.Quantity, dec_err: units.Quantity, kron_radius: float, image_path: str, good_image_path: str = None, separation_from_given: units.Quantity = None, epoch_date: str = None, class_star: float = None, mag_psf: units.Quantity = None, mag_psf_err: units.Quantity = None, snr_psf: float = None, image_depth: units.Quantity = None, do_mask: bool = True, **kwargs ): if good_image_path is None: good_image_path = image_path if isinstance(epoch_date, time.Time): epoch_date = epoch_date.strftime('%Y-%m-%d') photometry = { "instrument": str(instrument), "filter": str(fil), "epoch_name": epoch_name, "mag": u.check_quantity(mag, unit=units.mag), "mag_err": u.check_quantity(mag_err, unit=units.mag), "snr": float(snr), "a": u.check_quantity(ellipse_a, unit=units.arcsec, convert=True), "a_err": u.check_quantity(ellipse_a_err, unit=units.arcsec, convert=True), "b": u.check_quantity(ellipse_b, unit=units.arcsec, convert=True), "b_err": u.check_quantity(ellipse_b_err, unit=units.arcsec, convert=True), "theta": u.check_quantity(ellipse_theta, unit=units.deg, convert=True), "theta_err": u.check_quantity(ellipse_theta_err, unit=units.deg, convert=True), "ra": u.check_quantity(ra, units.deg, convert=True), "ra_err": u.check_quantity(ra_err, units.deg, convert=True), "dec": u.check_quantity(dec, units.deg, convert=True), "dec_err": u.check_quantity(dec_err, units.deg, convert=True), "kron_radius": float(kron_radius), "separation_from_given": u.check_quantity(separation_from_given, units.arcsec, convert=True), "epoch_date": str(epoch_date), "class_star": float(class_star), "mag_psf": u.check_quantity(mag_psf, unit=units.mag), "mag_psf_err": u.check_quantity(mag_psf_err, unit=units.mag), "snr_psf": snr_psf, "image_depth": u.check_quantity(image_depth, unit=units.mag), "image_path": image_path, "good_image_path": good_image_path, "do_mask": do_mask, } kwargs.update(photometry) if instrument not in self.photometry: self.photometry[instrument] = {} if fil not in self.photometry[instrument]: self.photometry[instrument][fil] = {} self.photometry[instrument][fil][epoch_name] = kwargs self.update_output_file() return kwargs