def energy_bounds(self): """Energy group bounds (`~astropy.units.Quantity`).""" energy = [_.energy_min for _ in self] energy.append(self[-1].energy_max) return Quantity(energy)
def time_coll(t): t = Quantity(t, 'yr') return (self._radius_free_expansion(t) - self.snr.radius_reverse_shock(t)).value
def time_coll(t): t = Quantity(t, "yr") r_pwn = self._radius_free_expansion(t).to("cm").value r_shock = self.snr.radius_reverse_shock(t).to("cm").value return r_pwn - r_shock
class EventListChecker(Checker): """Event list checker. Data format specification: ref:`gadf:iact-events` Parameters ---------- event_list : `~gammapy.data.EventList` Event list """ CHECKS = { "meta": "check_meta", "columns": "check_columns", "times": "check_times", "coordinates_galactic": "check_coordinates_galactic", "coordinates_altaz": "check_coordinates_altaz", } accuracy = {"angle": Angle("1 arcsec"), "time": Quantity(1, "microsecond")} # https://gamma-astro-data-formats.readthedocs.io/en/latest/events/events.html#mandatory-header-keywords meta_required = [ "HDUCLASS", "HDUDOC", "HDUVERS", "HDUCLAS1", "OBS_ID", "TSTART", "TSTOP", "ONTIME", "LIVETIME", "DEADC", "RA_PNT", "DEC_PNT", # TODO: what to do about these? # They are currently listed as required in the spec, # but I think we should just require ICRS and those # are irrelevant, should not be used. # 'RADECSYS', # 'EQUINOX', "ORIGIN", "TELESCOP", "INSTRUME", "CREATOR", # https://gamma-astro-data-formats.readthedocs.io/en/latest/general/time.html#time-formats "MJDREFI", "MJDREFF", "TIMEUNIT", "TIMESYS", "TIMEREF", # https://gamma-astro-data-formats.readthedocs.io/en/latest/general/coordinates.html#coords-location "GEOLON", "GEOLAT", "ALTITUDE", ] _col = namedtuple("col", ["name", "unit"]) columns_required = [ _col(name="EVENT_ID", unit=""), _col(name="TIME", unit="s"), _col(name="RA", unit="deg"), _col(name="DEC", unit="deg"), _col(name="ENERGY", unit="TeV"), ] def __init__(self, event_list): self.event_list = event_list def _record(self, level="info", msg=None): obs_id = self.event_list.table.meta["OBS_ID"] return {"level": level, "obs_id": obs_id, "msg": msg} def check_meta(self): meta_missing = sorted( set(self.meta_required) - set(self.event_list.table.meta)) if meta_missing: yield self._record( level="error", msg="Missing meta keys: {!r}".format(meta_missing)) def check_columns(self): t = self.event_list.table if len(t) == 0: yield self._record(level="error", msg="Events table has zero rows") for name, unit in self.columns_required: if name not in t.colnames: yield self._record( level="error", msg="Missing table column: {!r}".format(name)) else: if Unit(unit) != (t[name].unit or ""): yield self._record( level="error", msg="Invalid unit for column: {!r}".format(name)) def check_times(self): dt = (self.event_list.time - self.event_list.observation_time_start).sec if dt.min() < self.accuracy["time"].to_value("s"): yield self._record(level="error", msg="Event times before obs start time") dt = (self.event_list.time - self.event_list.observation_time_end).sec if dt.max() > self.accuracy["time"].to_value("s"): yield self._record(level="error", msg="Event times after the obs end time") if np.min(np.diff(dt)) <= 0: yield self._record(level="error", msg="Events are not time-ordered.") def check_coordinates_galactic(self): """Check if RA / DEC matches GLON / GLAT.""" t = self.event_list.table if "GLON" not in t.colnames: return galactic = SkyCoord(t["GLON"], t["GLAT"], unit="deg", frame="galactic") separation = self.event_list.radec.separation(galactic).to("arcsec") if separation.max() > self.accuracy["angle"]: yield self._record(level="error", msg="GLON / GLAT not consistent with RA / DEC") def check_coordinates_altaz(self): """Check if ALT / AZ matches RA / DEC.""" t = self.event_list.table if "AZ" not in t.colnames: return altaz_astropy = self.event_list.altaz separation = angular_separation( altaz_astropy.data.lon, altaz_astropy.data.lat, t["AZ"].quantity, t["ALT"].quantity, ) if separation.max() > self.accuracy["angle"]: yield self._record(level="error", msg="ALT / AZ not consistent with RA / DEC")
def test_special_profiles(self): for _profile_name in [ 'profile_lowlat', 'profile_midlat_summer', 'profile_midlat_winter', 'profile_highlat_summer', 'profile_highlat_winter' ]: _prof_func = getattr(atm, _profile_name) heights = Quantity(HEIGHTS_PROFILE, apu.km) consts = globals()[_profile_name.upper()] print(_profile_name, consts) ( c_temperatures, c_pressures, c_rho_water, c_pressures_water, c_ref_indices, c_humidities_water, c_humidities_ice, ) = consts with pytest.raises(TypeError): _prof_func(50) with pytest.raises(apu.UnitsError): _prof_func(50 * apu.Hz) with pytest.raises(AssertionError): _prof_func(-1 * apu.km) with pytest.raises(AssertionError): _prof_func(101 * apu.km) ( temperatures, pressures, rho_water, pressures_water, ref_indices, humidities_water, humidities_ice, ) = _prof_func(heights) assert_quantity_allclose( temperatures, Quantity(c_temperatures, apu.K) ) assert_quantity_allclose( pressures, Quantity(c_pressures, apu.hPa) ) assert_quantity_allclose( rho_water, Quantity(c_rho_water, apu.g / apu.m ** 3) ) assert_quantity_allclose( pressures_water, Quantity(c_pressures_water, apu.hPa) ) assert_quantity_allclose( ref_indices, Quantity(c_ref_indices, cnv.dimless) ) assert_quantity_allclose( humidities_water, Quantity(c_humidities_water, apu.percent) ) assert_quantity_allclose( humidities_ice, Quantity(c_humidities_ice, apu.percent) )
def time(self): """Time array (`~astropy.time.Time`).""" met = Quantity(self.table["TIME"].astype("float64"), "second") time = self.time_ref + met return time.tt
def observation_time_start(self): """Observation start time (`~astropy.time.Time`).""" return self.time_ref + Quantity(self.table.meta["TSTART"], "second")
def hillas_parameters(geom, image): """ Compute Hillas parameters for a given shower image. Implementation uses a PCA analogous to the implementation in src/main/java/fact/features/HillasParameters.java from https://github.com/fact-project/fact-tools The image passed to this function can be in three forms: >>> from ctapipe.image.hillas import hillas_parameters >>> from ctapipe.image.tests.test_hillas import create_sample_image, compare_hillas >>> geom, image, clean_mask = create_sample_image(psi='0d') >>> >>> # Fastest >>> geom_selected = geom[clean_mask] >>> image_selected = image[clean_mask] >>> hillas_selected = hillas_parameters(geom_selected, image_selected) >>> >>> # Mid (1.45 times longer than fastest) >>> image_zeros = image.copy() >>> image_zeros[~clean_mask] = 0 >>> hillas_zeros = hillas_parameters(geom, image_zeros) >>> >>> # Slowest (1.51 times longer than fastest) >>> image_masked = np.ma.masked_array(image, mask=~clean_mask) >>> hillas_masked = hillas_parameters(geom, image_masked) >>> >>> compare_hillas(hillas_selected, hillas_zeros) >>> compare_hillas(hillas_selected, hillas_masked) Each method gives the same result, but vary in efficiency Parameters ---------- geom: ctapipe.instrument.CameraGeometry Camera geometry image : array_like Charge in each pixel Returns ------- HillasParametersContainer: container of hillas parametesr """ unit = geom.pix_x.unit pix_x = Quantity(np.asanyarray(geom.pix_x, dtype=np.float64)).value pix_y = Quantity(np.asanyarray(geom.pix_y, dtype=np.float64)).value image = np.asanyarray(image, dtype=np.float64) image = np.ma.filled(image, 0) msg = 'Image and pixel shape do not match' assert pix_x.shape == pix_y.shape == image.shape, msg size = np.sum(image) if size == 0.0: raise HillasParameterizationError( 'size=0, cannot calculate HillasParameters') # calculate the cog as the mean of the coordinates weighted with the image cog_x = np.average(pix_x, weights=image) cog_y = np.average(pix_y, weights=image) # polar coordinates of the cog cog_r = np.linalg.norm([cog_x, cog_y]) cog_phi = np.arctan2(cog_y, cog_x) # do the PCA for the hillas parameters delta_x = pix_x - cog_x delta_y = pix_y - cog_y # The ddof=0 makes this comparable to the other methods, # but ddof=1 should be more correct, mostly affects small showers # on a percent level cov = np.cov(delta_x, delta_y, aweights=image, ddof=0) eig_vals, eig_vecs = np.linalg.eigh(cov) # round eig_vals to get rid of nans when eig val is something like -8.47032947e-22 near_zero = np.isclose(eig_vals, 0, atol=HILLAS_ATOL) eig_vals[near_zero] = 0 # width and length are eigen values of the PCA width, length = np.sqrt(eig_vals) # psi is the angle of the eigenvector to length to the x-axis vx, vy = eig_vecs[0, 1], eig_vecs[1, 1] # avoid divide by 0 warnings if length == 0: psi = skewness_long = kurtosis_long = np.nan else: if vx != 0: psi = np.arctan(vy / vx) else: psi = np.pi / 2 # calculate higher order moments along shower axes longitudinal = delta_x * np.cos(psi) + delta_y * np.sin(psi) m3_long = np.average(longitudinal**3, weights=image) skewness_long = m3_long / length**3 m4_long = np.average(longitudinal**4, weights=image) kurtosis_long = m4_long / length**4 return HillasParametersContainer( x=u.Quantity(cog_x, unit), y=u.Quantity(cog_y, unit), r=u.Quantity(cog_r, unit), phi=Angle(cog_phi, unit=u.rad), intensity=size, length=u.Quantity(length, unit), width=u.Quantity(width, unit), psi=Angle(psi, unit=u.rad), skewness=skewness_long, kurtosis=kurtosis_long, )
def to_quantity(self): """ Convert image to `~astropy.units.Quantity`. """ return Quantity(self.data, self.unit)
def test_cosmic_ray_flux(): energy = Quantity(1, "TeV") actual = cosmic_ray_flux(energy, "proton") desired = Quantity(0.096, "m-2 s-1 sr-1 TeV-1") assert_quantity_allclose(actual, desired)
# Licensed under a 3-clause BSD style license - see LICENSE.rst from __future__ import absolute_import, division, print_function, unicode_literals from numpy.testing import assert_allclose import numpy as np from astropy.units import Quantity from ...source import SNR, SNRTrueloveMcKee t = Quantity([0, 1, 10, 100, 1000, 10000], 'yr') snr = SNR() snr_mckee = SNRTrueloveMcKee() def test_SNR_luminosity_tev(): """Test SNR luminosity""" reference = [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.07680000e+33, 1.07680000e+33 ] assert_allclose(snr.luminosity_tev(t).value, reference) def test_SNR_radius(): """Test SNR radius""" reference = [ 0.00000000e+00, 3.08567758e+16, 3.08567758e+17, 3.08567758e+18, 1.17481246e+19, 2.95099547e+19 ] assert_allclose(snr.radius(t).value, reference) def test_SNR_radius_inner():
new_map = healpix_utils.load_map( '{}/Stokes{}_average_map_empirical_rm_in_eor0.fits'.format( sourcedir, pol_name)) maps.append(new_map) # Check that the pixel arrays are identical for pol_ind in range(len(pols) - 1): if np.max(np.abs(maps[pol_ind].pix_arr - maps[pol_ind + 1].pix_arr)) != 0: print('ERROR: Mismatched pixel arrays.') sys.exit(1) Ncomponents = np.shape(maps[0].signal_arr)[0] stokes = np.zeros((4, 1, Ncomponents)) for pol_ind in range(len(pols)): stokes[pol_ind, :, :] = maps[pol_ind].signal_arr stokes = Quantity(stokes, 'Jy/sr') freq_array = Quantity([182000000], 'Hz') if maps[0].nest: hpx_order = 'nested' else: hpx_order = 'ring' skymodel = pyradiosky.SkyModel(component_type='healpix', spectral_type='flat', stokes=stokes, freq_array=freq_array, hpx_inds=maps[0].pix_arr, hpx_order=hpx_order, nside=maps[0].nside) skymodel.write_skyh5('/Users/ruby/EoR/diffuse_map.skyh5')
def catalog_image(reference, psf, catalog='1FHL', source_type='point', total_flux=False, sim_table=None): """Creates an image from a simulated catalog, or from 1FHL or 2FGL sources. Parameters ---------- reference : `~astropy.io.fits.ImageHDU` Reference Image HDU. The output takes the shape and resolution of this. psf : `~gammapy.irf.EnergyDependentTablePSF` Energy dependent Table PSF object for image convolution. catalog : {'1FHL', '2FGL', 'simulation'} Flag which source catalog is to be used to create the image. If 'simulation' is used, sim_table must also be provided. source_type : {'point', 'extended', 'all'} Specify whether point or extended sources should be included, or both. TODO: Currently only 'point' is implemented. total_flux : bool Specify whether to conserve total flux. sim_table : `~astropy.table.Table` Table of simulated point sources. Only required if ``catalog='simulation'`` Returns ------- out_cube : `~gammapy.data.SkyCube` 2D Spectral cube containing the image. Notes ----- This is currently only implemented for a single energy band. """ from scipy.ndimage import convolve # This import is here instead of at the top to avoid an ImportError # due to circular dependencies from ..cube import SkyCube from ..spectrum import LogEnergyAxis wcs = WCS(reference.header) # Uses dummy energy for now to construct spectral cube # TODO : Fix this hack reference_cube = SkyCube(data=Quantity(np.array(reference.data), ''), wcs=wcs) if source_type == 'extended': raise NotImplementedError # TODO: Currently fluxes are not correct for extended sources. new_image = _extended_image(catalog, reference_cube) elif source_type == 'point': new_image, energy = _source_image(catalog, reference_cube, sim_table, total_flux) elif source_type == 'all': raise NotImplementedError # TODO: Currently Extended Sources do not work extended = _extended_image(catalog, reference_cube) point_source = _source_image(catalog, reference_cube, total_flux=True)[0] new_image = extended + point_source else: raise ValueError total_point_image = SkyCube(data=new_image, wcs=wcs, energy_axis=LogEnergyAxis(energy)) convolved_cube = new_image.copy() psf = psf.table_psf_in_energy_band(Quantity([np.min(energy).value, np.max(energy).value], energy.unit)) resolution = abs(reference.header['CDELT1']) ref = total_point_image.sky_image_ref kernel_array = psf.kernel(ref, normalize=True) convolved_cube = convolve(new_image, kernel_array, mode='constant') out_cube = SkyCube(data=Quantity(convolved_cube, ''), wcs=total_point_image.wcs, energy_axis=LogEnergyAxis(energy)) return out_cube
def catalog_table(catalog, energy_bands=False): """Creates catalog table from published source catalog. This creates a table of catalog sources, positions and fluxes for an indicated published source catalog - either 1FHL or 2FGL. This should be used to in instances where a table is required, for instance as an input for the `~gammapy.image.catalog_image` function. Parameters ---------- catalog : {'1FHL', '2FGL'} Catalog to load. energy_bands : bool Whether to return catalog in energy bands. Returns ------- table : `~astropy.table.Table` Point source catalog table. """ # This import is here instead of at the top to avoid an ImportError # due to circular dependencies from ..catalog import fetch_fermi_catalog data = [] cat_table = fetch_fermi_catalog(catalog, 'LAT_Point_Source_Catalog') for source in np.arange(len(cat_table)): glon = cat_table['GLON'][source] glat = cat_table['GLAT'][source] # Different from here between each catalog because of different catalog header names if catalog in ['1FHL', 'simulation']: energy = Quantity([10, 30, 100, 500], 'GeV') if energy_bands: Flux_10_30 = cat_table['Flux10_30GeV'][source] Flux_30_100 = cat_table['Flux30_100GeV'][source] Flux_100_500 = cat_table['Flux100_500GeV'][source] row = dict(Source_Type='PointSource', GLON=glon, GLAT=glat, Flux10_30=Flux_10_30, Flux30_100=Flux_30_100, Flux100_500=Flux_100_500) else: flux_bol = cat_table['Flux'][source] row = dict(Source_Type='PointSource', GLON=glon, GLAT=glat, flux=flux_bol) elif catalog == '2FGL': energy = Quantity([30, 100, 300, 1000, 3000, 10000, 100000], 'GeV') if not energy_bands: flux_bol = cat_table['Flux_Density'][source] row = dict(Source_Type='PointSource', GLON=glon, GLAT=glat, flux=flux_bol) else: Flux_30_100 = cat_table['Flux30_100'][source] Flux_100_300 = cat_table['Flux100_300'][source] Flux_300_1000 = cat_table['Flux300_1000'][source] Flux_1000_3000 = cat_table['Flux1000_3000'][source] Flux_3000_10000 = cat_table['Flux3000_10000'][source] Flux_10000_100000 = cat_table['Flux10000_100000'][source] row = dict(Source_Type='PointSource', Source_Name=source, GLON=glon, GLAT=glat, Flux_30_100=Flux_30_100, Flux_100_300=Flux_100_300, Flux_300_1000=Flux_300_1000, Flux_1000_3000=Flux_1000_3000, Flux_3000_10000=Flux_3000_10000, Flux_10000_100000=Flux_10000_100000) data.append(row) table = Table(data) table.meta['Energy Bins'] = energy return table
def inverse(self, value): return Quantity(value, self.unit, copy=False)
def pointing_zen(self): """Pointing zenith angle sky (`~astropy.units.Quantity`).""" return Quantity(self.obs_info.get("ZEN_PNT", np.nan), unit="deg")
def time_stop(self): """Stop time (`~astropy.time.Time`).""" t_stop = Quantity(self.meta["TSTOP"], "second") return self.time_ref + t_stop
def thickness(a): return Quantity(alt_to_thickness(a.to('m')), 'g cm-2')
def time_start(self): """Start time (`~astropy.time.Time`).""" t_start = Quantity(self.meta["TSTART"], "second") return self.time_ref + t_start
def altitude(a): return Quantity(thickness_to_alt(a.to('g cm-2')), 'm')
def observation_time_end(self): """Observation stop time (`~astropy.time.Time`).""" return self.time_ref + Quantity(self.table.meta["TSTOP"], "second")
# -- test functions ----------------------------------------------------------- @pytest.mark.parametrize( 'in_, out', [ (1126259462, int(GW150914)), (LIGOTimeGPS(1126259462, 391000000), GW150914), ('0', 0), ('Jan 1 2017', 1167264018), ('Sep 14 2015 09:50:45.391', GW150914), ((2017, 1, 1), 1167264018), (datetime(2017, 1, 1), 1167264018), (Time(57754, format='mjd'), 1167264018), (Time(57754.0001, format='mjd'), LIGOTimeGPS(1167264026, 640000000)), (Quantity(1167264018, 's'), 1167264018), (Decimal('1126259462.391000000'), GW150914), pytest.param(GlueGPS(GW150914.gpsSeconds, GW150914.gpsNanoSeconds), GW150914, marks=skipglue), (numpy.int32(NOW), NOW), # fails with lal-6.18.0 ('now', NOW), ('today', TODAY), ('tomorrow', TOMORROW), ('yesterday', YESTERDAY), (Quantity(1, 'm'), UnitConversionError), ('random string', (ValueError, TypeError)), ]) def test_to_gps(in_, out): """Test :func:`gwpy.time.to_gps` """
def test_profile_standard(self): args_list = [ (0, 84.99999999, apu.km), ] check_astro_quantities(atm.profile_standard, args_list) # also testing multi-dim arrays: heights = Quantity([[1, 10], [3, 20], [30, 50]], apu.km) ( temperatures, pressures, rho_water, pressures_water, ref_indices, humidities_water, humidities_ice, ) = atm.profile_standard(heights) assert_quantity_allclose( temperatures, Quantity([ [281.65, 223.15], [268.65, 216.65], [226.65, 270.65], ], apu.K) ) assert_quantity_allclose( pressures, Quantity([ [8.98746319e+02, 2.64364701e+02], [7.01086918e+02, 5.47497974e+01], [1.17189629e+01, 7.59478828e-01] ], apu.hPa) ) assert_quantity_allclose( rho_water, Quantity([ [4.54897995e+00, 5.05346025e-02], [1.67347620e+00, 3.40499473e-04], [2.24089942e-05, 1.21617633e-06] ], apu.g / apu.m ** 3) ) assert_quantity_allclose( pressures_water, Quantity([ [5.91241441e+00, 5.20387473e-02], [2.07466258e+00, 3.40420909e-04], [2.34379258e-05, 1.51895766e-06] ], apu.hPa) ) assert_quantity_allclose( ref_indices, Quantity([ [1.00027544, 1.00009232], [1.00021324, 1.00001961], [1.00000401, 1.00000022] ], cnv.dimless) ) assert_quantity_allclose( humidities_water, Quantity([ [5.30812596e+01, 8.12224381e+01], [4.72174462e+01, 1.14582635e+00], [2.47241153e-02, 2.98350282e-05] ], apu.percent) ) assert_quantity_allclose( humidities_ice, Quantity([ [4.89102920e+01, 1.31884489e+02], [4.93347383e+01, 1.97403148e+00], [3.88650989e-02, 3.05857185e-05] ], apu.percent) )
def __init__(self, **kwargs): self.energy_min = kwargs.pop("energy_min", Quantity(0, "TeV")) self.energy_max = kwargs.pop("energy_max", Quantity(0, "TeV")) super().__init__(**kwargs)
"""Plot SNR brightness evolution.""" import numpy as np import matplotlib.pyplot as plt from astropy.units import Quantity from gammapy.astro.source import SNR densities = Quantity([1, 0.1], 'cm^-3') t = Quantity(np.logspace(0, 5, 100), 'yr') for density in densities: snr = SNR(n_ISM=density) F = snr.luminosity_tev(t) / (4 * np.pi * Quantity(1, 'kpc')**2) plt.plot(t.value, F.to('ph s^-1 cm^-2').value, label='n_ISM = {0}'.format(density.value)) plt.vlines(snr.sedov_taylor_begin.to('yr').value, 1E-13, 1E-10, linestyle='--') plt.vlines(snr.sedov_taylor_end.to('yr').value, 1E-13, 1E-10, linestyle='--') plt.xlim(1E2, 1E5) plt.ylim(1E-13, 1E-10) plt.xlabel('time [years]') plt.ylabel('flux @ 1kpc [ph s^-1 cm ^-2]') plt.legend(loc=4) plt.loglog() plt.show()
def total_livetime(self): """Summed livetime""" livetimes = [o.livetime.to_value("s") for o in self] return Quantity(np.sum(livetimes), "s")
# Licensed under a 3-clause BSD style license - see LICENSE.rst from __future__ import absolute_import, division, print_function, unicode_literals import numpy as np from numpy.testing.utils import assert_allclose from astropy.utils.data import get_pkg_data_filename from astropy.io import fits from astropy.units import Quantity from astropy.tests.helper import pytest from astropy.coordinates import Angle from ...utils.testing import requires_dependency, requires_data from ...irf import EnergyDependentMultiGaussPSF from ...datasets import gammapy_extra ENERGIES = Quantity([1, 10, 25], 'TeV') @requires_dependency('scipy') @requires_data('gammapy-extra') def test_EnergyDependentMultiGaussPSF(): filename = get_pkg_data_filename('data/psf_info.txt') info_str = open(filename, 'r').read() filename = gammapy_extra.filename('test_datasets/unbundled/irfs/psf.fits') psf = EnergyDependentMultiGaussPSF.read(filename, hdu='POINT SPREAD FUNCTION') assert psf.info() == info_str @requires_data('gammapy-extra') def test_EnergyDependentMultiGaussPSF_write(tmpdir): filename = gammapy_extra.filename('test_datasets/unbundled/irfs/psf.fits')
def cube_sed(cube, mask=None, flux_type='differential', counts=None, errors=False, standard_error=0.1, spectral_index=2.3): """Creates SED from SpectralCube within given lat and lon range. Parameters ---------- cube : `~gammapy.data.SpectralCube` Spectral cube of either differential or integral fluxes (specified with flux_type) mask : array_like, optional 2D mask array, matching spatial dimensions of input cube. A mask value of True indicates a value that should be ignored, while a mask value of False indicates a valid value. flux_type : {'differential', 'integral'} Specify whether input cube includes differential or integral fluxes. counts : `~gammapy.data.SpectralCube`, optional Counts cube to allow Poisson errors to be calculated. If not provided, a standard_error should be provided, or zero errors will be returned. errors : bool If True, computes errors, if possible, according to provided inputs. If False (default), returns all errors as zero. standard_error : float If counts cube not provided, but error values required, this specifies a standard fractional error to be applied to values. Default = 0.1. spectral_index : float If integral flux is provided, this is used to calculate differential fluxes and energies (according to the Lafferty & Wyatt model-based method, assuming a power-law model). Returns ------- table : `~astropy.table.Table` A spectral energy table of energies, differential fluxes and differential flux errors. Units as those input. """ lon, lat = cube.spatial_coordinate_images values = [] for i in np.arange(cube.data.shape[0]): if mask is None: bin = cube.data[i].sum() else: bin = cube.data[i][mask].sum() values.append(bin.value) values = np.array(values) if errors: if counts is None: # Counts cube required to calculate poisson errors errors = np.ones_like([values]) * standard_error else: errors = [] for i in np.arange(counts.data.shape[0]): if mask is None: bin = counts.data[i].sum() else: bin = counts.data[i][mask].sum() r_error = 1. / (np.sqrt(bin.value)) errors.append(r_error) errors = np.array([errors]) else: errors = np.zeros_like([values]) if flux_type == 'differential': energy = cube.energy table = Table() table['ENERGY'] = energy, table['DIFF_FLUX'] = Quantity(values, cube.data.unit), table['DIFF_FLUX_ERR'] = Quantity(errors * values, cube.data.unit) elif flux_type == 'integral': emins = cube.energy[:-1] emaxs = cube.energy[1:] table = compute_differential_flux_points(x_method='lafferty', y_method='power_law', spectral_index=spectral_index, energy_min=emins, energy_max=emaxs, int_flux=values, int_flux_err=errors * values) else: raise ValueError('Unknown flux_type: {0}'.format(flux_type)) return table
def __init__(self, atom_data, ionization_data, levels=None, lines=None, macro_atom_data=None, macro_atom_references=None, zeta_data=None, collision_data=None, collision_data_temperatures=None, synpp_refs=None, photoionization_data=None): self.prepared = False # CONVERT VALUES TO CGS UNITS # Convert atomic masses to CGS # We have to use constants.u because astropy uses # different values for the unit u and the constant. # This is changed in later versions of astropy ( # the value of constants.u is used in all cases) if u.u.cgs == const.u.cgs: atom_data.loc[:, "mass"] = Quantity(atom_data["mass"].values, "u").cgs else: atom_data.loc[:, "mass"] = atom_data["mass"].values * const.u.cgs # Convert ionization energies to CGS ionization_data = ionization_data.squeeze() ionization_data[:] = Quantity(ionization_data[:], "eV").cgs # Convert energy to CGS levels.loc[:, "energy"] = Quantity(levels["energy"].values, 'eV').cgs # Create a new columns with wavelengths in the CGS units lines.loc[:, 'wavelength_cm'] = Quantity(lines['wavelength'], 'angstrom').cgs # SET ATTRIBUTES self.atom_data = atom_data self.ionization_data = ionization_data self.levels = levels self.lines = lines # Rename these (drop "_all") when `prepare_atom_data` is removed! self.macro_atom_data_all = macro_atom_data self.macro_atom_references_all = macro_atom_references self.zeta_data = zeta_data self.collision_data = collision_data self.collision_data_temperatures = collision_data_temperatures self.synpp_refs = synpp_refs self.photoionization_data = photoionization_data self._check_related() self.symbol2atomic_number = OrderedDict( zip(self.atom_data['symbol'].values, self.atom_data.index)) self.atomic_number2symbol = OrderedDict( zip(self.atom_data.index, self.atom_data['symbol']))
def energy_range(self): """Total energy range (`~astropy.units.Quantity` of length 2).""" return Quantity([self[0].energy_min, self[-1].energy_max])