"""This module contains classes that can be utilized for visualizing and
analyzing the simulated images and spectra"""
from photutils import aperture as aper
from matplotlib import colors as col
import matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator
from photutils.detection import DAOStarFinder
from photutils.psf import PSFPhotometry, SourceGrouper, ImagePSF
from photutils.background import Background2D, LocalBackground, MMMBackground
from photutils.aperture import aperture_photometry
from photutils.aperture import CircularAperture, CircularAnnulus, ApertureStats
from astropy.io import fits
import astropy.units as u
from astropy.io.fits import CompImageHDU
from astropy.modeling.fitting import LevMarLSQFitter
from astropy.stats import SigmaClip, sigma_clipped_stats
from astropy.coordinates import SkyCoord
from astropy.table import Table, join_skycoord, join
import numpy as np
[docs]
class Analyzer(object):
def __init__(self):
"""
A class to visualize and analyze the simulated image
Parameters
----------
Returns
-------
None.
"""
def __call__(self, df=None, wcs=None, data=None,
photometry=None, detect_sources=False, fwhm=3, sigma=13,
ZP=None):
"""
Performs sim simulation and sim Photometry
Imager.call()
do_photometry : Bool, Default : True
Do Aperture Photometry
"""
self.photometry_type = photometry
if photometry == 'Aper':
print("Running Aperture Photometry using Photutils")
self.aper_photometry(data, wcs, df, fwhm, sigma, ZP,
detect_sources)
elif photometry == 'PSF':
print("Running PSF Photometry using Photutils")
self.psf_photometry(data, wcs, df, fwhm, sigma, ZP,
detect_sources)
[docs]
def aper_photometry(self, data, wcs, df, fwhm, sigma, ZP,detect):
"""
Function to perform Aperture photometry
Parameters
----------
data: np.ndarray,
image to perform photometry on
wcs: astropy.wcs.WCS
WCS object of the image
df: pandas.DataFrame,
Source catalog of source in the image from simulation for
reference
fwhm : float, pixels
During aperture photometry,
fwhm corresponds to FWHM circular aperture for
aperture photometry
During PSF photometry,
fwhm corresponds FWHM kernel to use for PSF photometry
sigma: float,
The numbers of standard deviations above which source has to be
detected
detect: bool,
If true, DARStarFinder is used to detect sources for aperture
photometry
if false, input catalog is used for getting positions
of sources for aperture photometry
ZP : float,
zero point of the telescope.
Returns
-------
phot_table: astropy.table.Table
table containing photometry of the souces
Columns
'x-centeroid'
'y-centeroid'
'sky'
'flux'
'mag_in'
'mag_out'
'mag_err'
'SNR'
"""
# if detect flag is set to True, detect sources in the image
if detect:
print("Running Source Detection")
mmm_bkg = MMMBackground()
sigma_clip = SigmaClip(sigma_lower=2.25, sigma_upper=2.00)
if self.Cal0:
print("Performing photometry on Level 1 data")
mask = np.where(np.isnan(data) | np.isinf(data) | \
(data<0),1, 0)
else:
print("Performing photometry on Level 0 data")
mask = np.where(data<0,1,0)
try:
bkg = Background2D(data, (64, 64),
filter_size=(3, 3),
sigma_clip=sigma_clip,
bkg_estimator=mmm_bkg,
# coverage_mask=mask,
fill_value = 0.0)
data_bkgsub = data.copy() - bkg.background
except:
print("Background Estimation failed")
data_bkgsub = data
_, _, std = sigma_clipped_stats(data_bkgsub)
daofind = DAOStarFinder(threshold=sigma*std, fwhm=fwhm)
sources = daofind(data_bkgsub, mask=mask)
# Get the source positions
positions = np.transpose((sources['xcentroid'],
sources['ycentroid']))
else:
# create SkyCoord object from ra and dec values in the dataframe
coords = np.array([df['ra'],df['dec']])
# convert the sky coordinates to pixel coordinates
pix = wcs.world_to_pixel_values(coords.T)
positions = np.array(pix)
# create circular aperture object
self.aps = CircularAperture(positions, r=2*fwhm)
# count number of pixels within the aperture
ap_pix = self.aps.area
# create circular annulus object
self.bags = CircularAnnulus(positions, r_in=3*fwhm,
r_out=10*fwhm)
# count number of pixels within the annulus
sky_median = ApertureStats(data, self.bags).median
# perform aperture photometry on the data
phot_table = aperture_photometry(data, self.aps)
# calculate sky flux. electrons
phot_table['sky_flux'] = sky_median*ap_pix*self.gain*u.electron
# calculate source flux
phot_table['flux'] = (self.gain*phot_table['aperture_sum'].value - \
phot_table['sky_flux'].value)*u.electron
# calculate error on the source flux
NE_2 = phot_table['flux'].value + phot_table['sky_flux'].value + \
(self.DC_array.mean() + self.det_params['RN']**2 + \
(self.gain/2)**2)*ap_pix
phot_table['flux_err'] = np.sqrt(NE_2)*u.electron
# calculate signal to noise ratio
phot_table['SNR'] = phot_table['flux']/phot_table['flux_err']
if not detect:
phot_table['ra'] = df['ra'].values
phot_table['dec'] = df['dec'].values
phot_table['mag_in'] = df['mag'].values
else:
coords = np.array(wcs.pixel_to_world_values(positions))
phot_table['SkyCoord'] = SkyCoord(ra = coords[:, 0],
dec = coords[:, 1],
unit = 'deg')
tab2 = Table.from_pandas(df)
tab2['SkyCoord'] = SkyCoord(ra = df['ra'],
dec = df['dec'],
unit = 'deg')
min_dist = join_skycoord(2*self.pixel_scale*u.arcsec)
phot_table = join(phot_table, tab2['mag', 'x', 'y', 'SkyCoord'],
join_funcs={'SkyCoord': min_dist})
phot_table.rename_column('mag', 'mag_in')
phot_table['mag_out'] = -2.5*np.log10(phot_table['flux'].value) + ZP
phot_table['mag_err'] = 1.082/phot_table['SNR'].value
self.phot_table = phot_table
[docs]
def psf_photometry(self, data, wcs, df, fwhm, sigma, ZP, detect=True):
"""
Perform PSF photometry.
Parameters
----------
data : ndarray
Image data.
wcs : astropy.wcs.WCS
WCS object.
df : pandas.DataFrame
Input catalog.
fwhm : float
PSF FWHM in pixels.
sigma : float
Detection threshold in sigma.
ZP : float
Photometric zeropoint.
detect_source : bool
If True, detect sources using DAOStarFinder.
If False, use input catalog positions.
"""
# -------------------------------------------------
# Image statistics
# -------------------------------------------------
mean, median, std = sigma_clipped_stats(data, sigma=3)
# -------------------------------------------------
# PSF model
# -------------------------------------------------
psf_model = ImagePSF(self.psf, oversampling=self.psf_oversamp)
self.psf_model = psf_model
# -------------------------------------------------
# Background estimation
# -------------------------------------------------
sigma_clip = SigmaClip(sigma=3)
bkgstat = MMMBackground(sigma_clip=sigma_clip)
localbkg_estimator = LocalBackground(
inner_radius=3*fwhm,
outer_radius=10*fwhm,
bkg_estimator=bkgstat
)
# -------------------------------------------------
# Source grouping
# -------------------------------------------------
grouper = SourceGrouper(3 * fwhm)
# -------------------------------------------------
# Fitter
# -------------------------------------------------
fitter = LevMarLSQFitter()
# -------------------------------------------------
# Source finder
# -------------------------------------------------
if detect:
print("Running Source Detection")
finder = DAOStarFinder(
threshold=sigma * std,
fwhm=fwhm
)
init_params = None
else:
finder = None
init_params = Table()
init_params['x_0'] = df['x']
init_params['y_0'] = df['y']
self.psf_model.x_0.fixed = True
self.psf_model.y_0.fixed = True
# -------------------------------------------------
# PSF photometry object
# -------------------------------------------------
photometry = PSFPhotometry(
psf_model=self.psf_model,
fitter=fitter,
finder=finder,
grouper=grouper,
localbkg_estimator=localbkg_estimator,
fit_shape=(5, 5),
aperture_radius=3 * fwhm,
progress_bar=True
)
# -------------------------------------------------
# Run photometry
# -------------------------------------------------
phot_table = photometry(data, init_params=init_params)
# -------------------------------------------------
# Remove failed fits
# -------------------------------------------------
if 'flags' in phot_table.colnames:
phot_table = phot_table[phot_table['flags'] == 0]
# -------------------------------------------------
# Convert to sky coordinates
# -------------------------------------------------
positions = np.vstack((phot_table['x_fit'], phot_table['y_fit'])).T
coords = np.array(wcs.pixel_to_world_values(positions))
# -------------------------------------------------
# Flux calculations
# -------------------------------------------------
ap_pix = 5 * 5
phot_table['flux_fit'] = self.gain * phot_table['flux_fit'] * u.electron
phot_table['flux_fit_err'] = self.gain * phot_table['flux_err'] * u.electron
NE_2 = (
phot_table['flux_fit'].value
+ phot_table['flux_fit_err'].value
+ (self.DC_array.mean()
+ self.det_params['RN']**2
+ (self.gain / 2)**2) * ap_pix
)
phot_table['flux_err'] = np.sqrt(NE_2) * u.electron
# -------------------------------------------------
# Signal-to-noise
# -------------------------------------------------
phot_table['SNR'] = phot_table['flux_fit'] / phot_table['flux_err']
# -------------------------------------------------
# Magnitudes
# -------------------------------------------------
phot_table['mag_out'] = -2.5 * np.log10(phot_table['flux_fit'].value) + ZP
phot_table['mag_err'] = 1.082 / phot_table['SNR'].value
# -------------------------------------------------
# Add SkyCoord
# -------------------------------------------------
phot_table['SkyCoord'] = SkyCoord(
ra=coords[:, 0],
dec=coords[:, 1],
unit='deg'
)
# -------------------------------------------------
# Match with input catalog
# -------------------------------------------------
tab2 = Table.from_pandas(df)
tab2['SkyCoord'] = SkyCoord(
ra=df['ra'],
dec=df['dec'],
unit='deg'
)
min_dist = join_skycoord(2 * self.pixel_scale * u.arcsec)
phot_table = join(
phot_table,
tab2['mag', 'x', 'y', 'SkyCoord'],
join_funcs={'SkyCoord': min_dist}
)
phot_table.rename_column('mag', 'mag_in')
# -------------------------------------------------
# Apertures for plotting
# -------------------------------------------------
positions = np.vstack((phot_table['x_fit'], phot_table['y_fit'])).T
self.aps = CircularAperture(positions, r=2 * fwhm)
self.phot_table = phot_table
return phot_table
[docs]
def show_field(self, figsize=(12, 10), marker='.', cmap='jet'):
"""
Function for creating a scatter plot of sources within the FoV
Parameters
----------
figsize : tuple,
Figure size
Returns
-------
fig, ax
"""
scale = self.n_x/self.n_y
figsize = figsize[0]*scale, figsize[1]
# Cropping Dataframe based on FoV
left = self.n_pix_psf
right = left + self.n_x_sim
df = self.sim_df
x_min_cut = (df['x'] > left)
x_max_cut = (df['x'] < right)
bottom = self.n_pix_psf
top = bottom + self.n_y_sim
y_min_cut = (df['y'] > bottom)
y_max_cut = (df['y'] < top)
fov_x = (self.n_x*self.pixel_scale)/3600
fov_y = (self.n_y*self.pixel_scale)/3600
fov_x = np.round(fov_x, 4)
fov_y = np.round(fov_y, 4)
fig, ax = plt.subplots(1, 1, figsize=figsize)
if hasattr(self, 'spec_bins'):
x = df['x']
y = df['y']
c = None
cmap = None
color = None
if 'mag' in df.keys():
c = df['mag']
cmap = 'jet'
color = None
img = ax.scatter(x, y, c=c, color = color, cmap=cmap, marker=marker)
cb = plt.colorbar(img, fraction = 1/scale, pad =0.01)
cb.set_label('mag (ABmag)')
ax.set_title(f"""Requested Center : {self.name} | {len(df)} sources
Fov(RA) : {fov_x} (deg) | Fov(Dec) : {fov_y} (deg)""")
ax.set_xlabel('x (pix)')
ax.set_ylabel('y (pix)')
if hasattr(self, 'L'):
B = self.B
L = self.L
l = len(self.spec_bins)
PA = self.PA
delta = np.arctan(B/L)
d = np.sqrt(L**2 + B**2)/2
om = PA - delta
x_corr = self.n_x_sim//2 - d*np.cos(om) - l/2
y_corr = self.n_y_sim//2 - d*np.sin(om) - B*np.cos(PA)
start = B*np.sin(PA) + l/2 + x_corr
end = start + L*np.cos(PA)
x = np.linspace(start,end,100)
y = np.tan(PA)*(x - B*np.sin(PA) - l/2 - x_corr) + y_corr
ax.plot(x,y,color = 'red')
start = l/2 + x_corr
end = start + L*np.cos(PA)
x = np.linspace(start, end, 100)
y = np.tan(PA)*(x - l/2 - x_corr) + B*np.cos(PA) + y_corr
ax.plot(x,y,color = 'red')
start = y_corr
end = start + B*np.cos(PA)
y = np.linspace(start, end, 100)
x = -np.tan(PA)*(y - B*np.cos(PA) - y_corr) + l/2 + x_corr
ax.plot(x,y,color = 'red')
start = y_corr + L*np.sin(PA)
end = start + B*np.cos(PA)
y = np.linspace(start, end, 100)
x = -np.tan(PA)*(y - B*np.cos(PA) - L*np.sin(PA) - y_corr) + L*np.cos(PA) + l/2 + x_corr
ax.plot(x,y,color = 'red')
ax.set_xlim(0,self.n_x_sim)
ax.set_ylim(0,self.n_y_sim)
else:
x = df['ra']
y = df['dec']
c = df['mag']
img = ax.scatter(x, y, c=c, marker=marker, cmap=cmap)
cb = plt.colorbar(img)
cb.set_label('mag (ABmag)')
ax.set_title(f"""Requested Center : {self.name} | {len(df)} sources
Fov(RA) : {fov_x} (deg) | Fov(Dec) : {fov_y} (deg)""")
ax.invert_xaxis()
ax.set_xlabel('RA (Degrees)')
ax.set_ylabel('Dec (Degrees)')
ax.invert_xaxis()
ax.set_xlim(self.ra+fov_x/2, self.ra-fov_x/2)
ax.set_ylim(self.dec-fov_y/2,self.dec+fov_y/2)
ax.xaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_minor_locator(AutoMinorLocator())
ax.tick_params(which='both', width=2,direction="in", top = True,right = True,
bottom = True, left = True)
return fig, ax
[docs]
def show_image(self, source='Digital', fig=None, ax=None, cmap='jet',
figsize=(15, 10), download=False, show_wcs=True,
overlay_apertures=False):
"""
Function for plotting the simulated field image
Source: str,
Choose from
'Digital' : Final digial image
'Charge' : electrons, Light(Source + sky) +
Dark Current + Noises
'Source' : Source + Sky + Noises
'Sky' : Sky + shot_noise
'DC' : Dark Current + DNFP
'QE' : Quantum efficiency fluctuation across
detector
'Bias' : Charge offset
'PRNU' : Photon Response Non-Uniformity
'DNFP' : Dark Noise Fixed Pattern
'QN' : Quantization Noise
'Resid' : Residual Image If PSF Photometry
was performed
fig : matplotlib.pyplot.figure
User defined figure
ax : matplotlib.pyplot.axes
User defined axes
cmap : str,
matplotlib.pyplot colormap
figsize : tuple
download : bool
show_wcs : bool
If true adds WCS projection to the image
Returns
-------
Image
fig, ax
"""
if hasattr(self,'digital'):
if fig is None or ax is None:
fig = plt.figure(figsize=figsize)
if show_wcs:
ax = fig.add_subplot(projection=self.wcs)
else:
ax = fig.add_subplot()
norm = None
if source == 'Digital':
data = self.digital.copy()
norm = col.LogNorm()
elif source == 'Charge':
data = self.charge.copy()
norm = col.LogNorm()
elif source == 'Source':
data = self.light_array.copy()
norm = col.LogNorm()
elif source == 'Sky':
data = self.sky_photons.copy()
elif source == 'DC':
data = self.DC_array.copy()
elif source == 'Bias':
data = self.bias_array.copy()
elif source == 'PRNU':
data = self.PRNU_array.copy()
elif source == 'DNFP':
norm = col.LogNorm()
data = self.DNFP_array.copy()
elif source == 'QN':
data = self.QN_array.copy()
elif source == 'Resid':
if self.photometry_type == "PSF":
data = self.resid.copy()
else:
print("Run PSF Photometry!")
return None, None
else:
print("Invalid Input")
return None, None
if data.min() < 0:
print('Negative values in image. Increase Bias')
data += data.min()
img = ax.imshow(data, cmap=cmap, norm=norm)
ax.grid(False)
cb = plt.colorbar(img, ax=ax)
cb.set_label('DN')
ax.set_title(f'{source} \nRequested center : {self.name}')
ax.grid(False)
if overlay_apertures:
if self.photometry_type == "Aper":
for aperture in self.aps:
if aperture is not None:
aperture.plot(ax=ax, color='red', lw=1.5)
for aperture in self.bags:
if aperture is not None:
aperture.plot(ax=ax, color='yellow', lw=1.5)
elif self.photometry_type == "PSF":
for aperture in self.aps:
if aperture is not None:
aperture.plot(ax=ax, color='red', lw=1.5)
if hasattr(self, 'L'):
B = self.B
L = self.L
l = len(self.spec_bins)
PA = self.PA
delta = np.arctan(B/L)
d = np.sqrt(L**2 + B**2)/2
om = PA - delta
x_corr = self.n_x//2 - d*np.cos(om) - l/2
y_corr = self.n_y//2 - d*np.sin(om) - B*np.cos(PA)
start = B*np.sin(PA) + l/2 + x_corr
end = start + L*np.cos(PA)
x = np.linspace(start,end,100)
y = np.tan(PA)*(x - B*np.sin(PA) - l/2 - x_corr) + y_corr
ax.plot(x,y,color = 'red')
start = l/2 + x_corr
end = start + L*np.cos(PA)
x = np.linspace(start, end, 100)
y = np.tan(PA)*(x - l/2 - x_corr) + B*np.cos(PA) + y_corr
ax.plot(x,y,color = 'red')
start = y_corr
end = start + B*np.cos(PA)
y = np.linspace(start, end, 100)
x = -np.tan(PA)*(y - B*np.cos(PA) - y_corr) + l/2 + x_corr
ax.plot(x,y,color = 'red')
start = y_corr + L*np.sin(PA)
end = start + B*np.cos(PA)
y = np.linspace(start, end, 100)
x = -np.tan(PA)*(y - B*np.cos(PA) - L*np.sin(PA) - y_corr) + L*np.cos(PA) + l/2 + x_corr
ax.plot(x,y,color = 'red')
if download:
fig.savefig(f"{source}.png", format='png')
return fig, ax
else:
print("Run Simulation")
[docs]
def show_hist(self, source='Digital', bins=None,
fig=None, ax=None, figsize=(15, 8)):
"""
Function for plotting histogram of various stages of simulation
Parameters
----------
Source: str,
Choose from
'Digital' : Final digial image
'Charge' : electrons, Light(Source + sky)
+ Dark Current + Noises
'Source' : Source + Sky + Noises
'Sky' : Sky + shot_noise
'DC' : Dark Current + DNFP
'QE' : Quantum efficiency fluctuation across
detector
'Bias' : Charge offset
'PRNU' : Photon Response Non-Uniformity
'DNFP' : Dark Noise Fixed Pattern
'QN' : Quantization Noise
bins : numpy.array,
bins for making histogram
fig : matplotlib.pyplot.figure
User defined figure
ax : matplotlib.pyplot.axes
User defined axes
figsize : tuple
"""
if hasattr(self,'digital'):
if fig is None or ax is None:
fig, ax = plt.subplots(1, 1, figsize=figsize)
if source == 'Digital':
data = self.digital.ravel()
elif source == 'Charge':
data = self.charge.ravel()
elif source == 'Source':
data = self.light_array
elif source == 'Sky':
data = self.sky_photons.ravel()
elif source == 'DC':
data = self.DC_array.ravel()
elif source == 'Bias':
data = self.bias_array.ravel()
elif source == 'PRNU':
data = self.PRNU_array.ravel()
elif source == 'DNFP':
data = self.DNFP_array.ravel()
elif source == 'QN':
data = self.QN_array.ravel()
if bins is None:
bins = np.linspace(data.min(), data.max(), 20)
ax.hist(data, bins=bins)
ax.set_title(f'{source} histogram')
ax.set_ylabel('Count')
ax.set_yscale('log')
return fig, ax
else:
print("Run Simulation")
[docs]
def getImage(self, source='Digital'):
"""
Function of retrieving image array at different stages of simulation.
Parameters
----------
Source: str,
Choose from
'Digital' : Final digial image
'Charge' : electrons, Light(Source + sky)
+ Dark Current + Noises
'Source' : Source + Sky + Noises
'Sky' : Sky + shot_noise
'DC' : Dark Current + DNFP
'QE' : Quantum efficiency fluctuation across
detector
'Bias' : Charge offset
'PRNU' : Photon Response Non-Uniformity
'DNFP' : Dark Noise Fixed Pattern
'QN' : Quantization Noise
"""
if hasattr(self, 'digital'):
if source == 'Digital':
data = self.digital
elif source == 'Charge':
data = self.charge
elif source == 'Sky':
data = self.sky_photoelec
elif source == 'DC':
data = self.DC_array
elif source == 'QE':
data = self.qe_array
elif source == 'Bias':
data = (self.bias_array + self.DC_array)
elif source == 'PRNU':
data = self.PRNU_array
elif source == 'DNFP':
data = self.DNFP_array
elif source == 'QN':
data = self.QN_array
else:
data = 0
return data
else:
print("Run Simulation")
[docs]
def writeto(self, name, source='Digital', user_source=None,
with_dark_flat = False):
"""
Function for downloading a fits file of simulated field image
Parameters
----------
name : str
filename, Example : simulation.fits
Source: str,
Choose from
'Digital' : Final digial image
'Charge' : electrons, Light(Source + sky)
+ Dark Current + Noises
'Source' : Source + Sky + Noises
'Sky' : Sky + shot_noise
'DC' : Dark Current + DNFP
'Bias' : Charge offset
'PRNU' : Photon Response Non-Uniformity
'DNFP' : Dark Noise Fixed Pattern
'QN' : Quantization Noise
user_source : numpy.ndarray
2D numpy array user wants to save as FITS
with_dark_flat : bool
True : Output fits will provide dark and flat frames
"""
if hasattr(self, 'digital'):
if user_source is not None and isinstance(user_source) == np.ndarray:
data = user_source
elif source == 'Digital':
data = self.digital
elif source == 'Charge':
data = self.charge
elif source == 'Source':
data = self.light_array
elif source == 'Sky':
data = self.sky_photoelec
elif source == 'DC':
data = self.DC_array
elif source == 'Bias':
data = self.bias_array
elif source == 'PRNU':
data = self.PRNU_array
elif source == 'DNFP':
data = self.DNFP_array
elif source == 'QN':
data = self.QN_array
else:
print(f"{source} is not a valid source")
hdu = fits.PrimaryHDU(data, header=self.header)
hdu.wcs = self.wcs
hdus = [hdu]
if with_dark_flat:
bias = self.bias_frame
header = self.header
header['FRAME'] = 'Bias'
hdu = fits.ImageHDU(bias, header=header)
hdus.append(hdu)
dark = self.dark_frame
header = self.header
header['FRAME'] = 'Dark'
hdu = fits.ImageHDU(dark, header=header)
hdus.append(hdu)
flat = self.flat_frame
header['FRAME'] = 'Flat'
hdu = fits.ImageHDU(flat, header=header)
hdus.append(hdu)
header['FRAME'] = 'PSF'
hdu = fits.ImageHDU(self.psf, header=header)
hdus.append(hdu)
hdul = fits.HDUList(hdus)
hdul.writeto(f'{name}', overwrite=True, checksum=True)
else:
print("Run Simulation")
[docs]
def writecomp(self, name):
hdu = CompImageHDU(data=self.digital, header=self.header)
hdus = [fits.PrimaryHDU(), hdu]
hdul = fits.HDUList(hdus)
hdul.writeto(name, overwrite = True, checksum = True)
[docs]
class SpecAnalyzer(object):
def __init__(self):
"""
A class to visualize and analyze the simulated image
Parameters
----------
Imager.init()
Returns
-------
None.
"""
[docs]
def cal_flux(self, flux, A=0, B=0.15):
"""
Calibrate Flux by converting flux in counts to ergs/s/cm2/A
"""
self.phot_table['flux'] = self.phot_table['flux']/self.UC