def plot_plane(planecolor=0.5, coord_system='gal', plane='SGP', **kwargs): """ Plot a the supergalactic plane onto skymap. :param planecolor: color of plane :param coord_system: default galactic ('gal') / equatorial ('eq') :param plane: plots 'SGP' or 'GAL' or both (list) into plot :param kwargs: additional named keyword arguments passed to plt.plot() """ phi0 = np.linspace(0, 2 * np.pi, 100) if coord_system.upper() == 'GAL': # only plotting the SGP makes sense phi, theta = coord.vec2ang( coord.sgal2gal(coord.ang2vec(phi0, np.zeros_like(phi0)))) kwargs.setdefault('color', planecolor) plt.plot(-np.sort(phi), theta[np.argsort(phi)], **kwargs) elif coord_system.upper() == 'EQ': if 'SGP' in plane: phi, theta = coord.vec2ang( coord.gal2eq( coord.sgal2gal(coord.ang2vec(phi0, np.zeros_like(phi0))))) kwargs.setdefault('color', planecolor) plt.plot(-np.sort(phi), theta[np.argsort(phi)], **kwargs) if 'GAL' in plane: phi, theta = coord.vec2ang( coord.gal2eq(coord.ang2vec(phi0, np.zeros_like(phi0)))) kwargs.setdefault('color', '0.5') plt.plot(-np.sort(phi), theta[np.argsort(phi)], **kwargs) else: raise Exception( "plane type not understood, use GP or SGP or list!") else: raise Exception("coord system not understood, use eq or gal!")
def test_07_smear_sources_dynamically(self): sim = ObservedBound(self.nside, self.nsets, self.ncrs) sim.set_energy(log10e_min=19.) sim.set_charges('AUGER') sim.set_sources(1) sim.set_rigidity_bins(np.arange(17., 20.5, 0.02)) sim.smear_sources(delta=0.1, dynamic=True) sim.arrival_setup(1.) crs = sim.get_data(convert_all=True) rigs = sim.rigidities rig_med = np.median(rigs) vecs1 = coord.ang2vec(crs['lon'][rigs >= rig_med], crs['lat'][rigs >= rig_med]) vecs2 = coord.ang2vec(crs['lon'][rigs < rig_med], crs['lat'][rigs < rig_med]) # Higher rigidities experience higher deflections self.assertTrue(np.mean(coord.angle(vecs1, sim.sources)) < np.mean(coord.angle(vecs2, sim.sources)))
def sensitivity_2pt(self, set_idx=None, niso=1000, bins=180, **kwargs): """ Function to calculate the sensitivity by the 2pt-auto-correlation over a scrambling of the right ascension coordinates. :param set_idx: If set, only this set number will be evaluated :param niso: Number of isotropic sets to calculate :param bins: Number of angular bins, 180 correspond to 1 degree binning (np.linspace(0, np.pi, bins+1). :param kwargs: additional named arguments passed to obs.two_pt_auto() :return: pvalues in the shape (self.nsets, bins) """ kwargs.setdefault('cumulative', True) vec_crs = self.get('vecs') _, dec = coord.vec2ang(coord.gal2eq(np.reshape(vec_crs, (3, -1)))) # calculate auto correlation for isotropic scrambled data _ac_iso = np.zeros((niso, bins)) for i in range(niso): _vecs = coord.ang2vec(coord.rand_phi(self.ncrs), np.random.choice(dec, size=self.ncrs)) _ac_iso[i] = obs.two_pt_auto(_vecs, bins, **kwargs) # calculate p-value by comparing the true sets with the isotropic ones set_idx = np.arange(self.nsets) if set_idx is None else [set_idx] pvals = np.zeros((len(set_idx), bins)) for i, idx in enumerate(set_idx): _ac_crs = obs.two_pt_auto(vec_crs[:, idx], bins, **kwargs) pvals[i] = np.sum(_ac_iso >= _ac_crs[np.newaxis], axis=0) / float(niso) return pvals
def test_06_rotate(self): v1 = coord.rand_vec(stat) rot_axis = np.hstack(coord.rand_vec(1)) angle = 0.25 v2 = coord.rotate(v1, rot_axis, angle) angles = coord.angle(v1, v2) self.assertTrue((angles > 0).all() & (angles <= angle).all()) # rotate back v3 = coord.rotate(v2, rot_axis, -angle) v4 = coord.rotate(v2, rot_axis, 2 * np.pi - angle) self.assertTrue(np.allclose(v1, v3)) self.assertTrue(np.allclose(v3, v4)) # when rotating around z-axis and vectors have z=0: all angles have to be 0.25 rot_axis = np.array([0, 0, 1]) v1 = coord.ang2vec(coord.rand_phi(stat), np.zeros(stat)) v2 = coord.rotate(v1, rot_axis, angle) angles = coord.angle(v1, v2) self.assertTrue((angles > angle - 1e-3).all() & (angles < angle + 1e-3).all()) # when rotating around z-axis all angles correspond to longitude shift angles = 2 * np.pi * np.random.random(stat) v1 = coord.rand_vec(stat) lon1, lat1 = coord.vec2ang(v1) v2 = np.array( [coord.rotate(vi, rot_axis, ai) for vi, ai in zip(v1.T, angles)]).T lon2, lat2 = coord.vec2ang(v2) self.assertTrue(np.allclose(lat1, lat2)) lon_diff = lon1 - lon2 lon_diff[lon_diff < 0] += 2 * np.pi self.assertTrue(np.allclose(lon_diff, angles))
def test_05_ang2vec(self): phi = coord.rand_phi(stat) theta = coord.rand_theta(stat) vec = coord.ang2vec(phi, theta) self.assertTrue(np.allclose(np.sum(vec**2, axis=0), np.ones(stat))) phi2, theta2 = coord.vec2ang(vec) self.assertTrue(np.allclose(phi, phi2)) self.assertTrue(np.allclose(theta, theta2))
def __init__(self, lon_roi, lat_roi, r_roi, ax=None, title=None, **kwargs): """ :param lon_roi: Longitude of center of ROI in radians (0..2*pi) :param lat_roi: Latitude of center of ROI in radians (0..2*pi) :param r_roi: Radius of ROI to be plotted (in radians) :param ax: Matplotlib axes in case you want to plot on certain axes :param title: Optional title of plot (plotted in upper left corner) :param kwargs: keywords passed to matplotlib.figure() """ from mpl_toolkits.basemap import Basemap # pylint: disable=import-error,no-name-in-module import matplotlib as mpl with_latex_style = { "text.usetex": True, "font.family": "serif", "axes.labelsize": 30, "font.size": 30, "legend.fontsize": 30, "xtick.labelsize": 26, "ytick.labelsize": 26, "legend.fancybox": False, "lines.linewidth": 3.0, "patch.linewidth": 3.0 } mpl.rcParams.update(with_latex_style) assert (isinstance(lon_roi, (float, int))) and (isinstance(lat_roi, (float, int))) and \ (isinstance(r_roi, (float, int))), "Keywords 'lon_roi', 'lat_roi' and 'r_roi' have to be floats or ints!" self.vec_0 = coord.ang2vec(lon_roi, lat_roi) self.lon_0 = np.rad2deg(lon_roi) self.lat_0 = np.rad2deg(lat_roi) self.r_roi = r_roi self.scale = 5500000 * (r_roi / 0.3) self.fig = None self.ax = ax if ax is None: kwargs.setdefault('figsize', [8, 8]) self.fig = plt.figure(**kwargs) self.ax = plt.axes() self.title = title if title is not None: self.text(0.02, 0.98, title, verticalalignment='top', fontsize=36) self.m = Basemap(width=self.scale, height=self.scale, resolution='l', projection='stere', celestial=True, lat_0=self.lat_0, lon_0=-360 - self.lon_0 if self.lon_0 < 0 else -self.lon_0, ax=ax)
def ang2vec(phi, theta): """ Substitutes healpy.ang2vec() to use our angle convention :param phi: longitude, range (pi, -pi), 0 points in x-direction, pi/2 in y-direction :param theta: latitude, range (pi/2, -pi/2), pi/2 points in z-direction :return: vector of shape (3, n) """ return coord.ang2vec(phi, theta)
def test_09_exposure(self): sim = ObservedBound(self.nside, self.nsets, self.ncrs) sim.apply_exposure() sim.arrival_setup(0.) crs = sim.get_data(convert_all=True) vecs_eq = coord.gal2eq(coord.ang2vec(np.hstack(crs['lon']), np.hstack(crs['lat']))) lon_eq, lat_eq = coord.vec2ang(vecs_eq) self.assertTrue(np.abs(np.mean(lon_eq)) < 0.05) self.assertTrue((np.mean(lat_eq) < -0.5) & (np.mean(lat_eq) > - 0.55))
def test_02_line(self): ncrs = 1000 roi_size = 0.25 lat = np.zeros(ncrs) lon = np.linspace(-roi_size, roi_size, ncrs) p = coord.ang2vec(lon, lat) T, N = obs.thrust(p, weights=None) self.assertTrue(np.abs(T[1] - 0.5 * roi_size) < 1e-3) self.assertTrue(np.abs(T[2]) < 1e-3)
def test_15_exposure(self): nsets = 100 sim = ObservedBound(self.nside, nsets, self.ncrs) sim.apply_exposure(a0=-35.25, zmax=60) sim.arrival_setup(0.2) crs = sim.get_data(convert_all=True) lon, lat = np.hstack(crs['lon']), np.hstack(crs['lat']) ra, dec = coord.vec2ang(coord.gal2eq(coord.ang2vec(lon, lat))) exp = coord.exposure_equatorial(dec, a0=-35.25, zmax=60) self.assertTrue((exp > 0).all())
def test_11_test_vecs_galactic(self): lon, lat = coord.rand_phi(stat), coord.rand_theta(stat) v = coord.ang2vec(lon, lat) self.assertTrue( np.allclose(lon, coord.get_longitude(v, coord_system='gal'))) self.assertTrue( np.allclose(lat, coord.get_latitude(v, coord_system='gal'))) v_eq = coord.gal2eq(v) self.assertTrue( np.allclose(lon, coord.get_longitude(v_eq, coord_system='eq'))) self.assertTrue( np.allclose(lat, coord.get_latitude(v_eq, coord_system='eq')))
def test_10_test_vecs_equatorial(self): ras, decs = coord.rand_phi(stat), coord.rand_theta(stat) v = coord.ang2vec(ras, decs) self.assertTrue( np.allclose(ras, coord.get_right_ascension(v, coord_system='eq'))) self.assertTrue( np.allclose(decs, coord.get_declination(v, coord_system='eq'))) v_gal = coord.eq2gal(v) self.assertTrue( np.allclose(ras, coord.get_right_ascension(v_gal, coord_system='gal'))) self.assertTrue( np.allclose(decs, coord.get_declination(v_gal, coord_system='gal')))
def test_19_apply_uncertainties(self): sim = ObservedBound(self.nside, self.nsets, self.ncrs) log10e = sim.set_energy(log10e_min=19., log10e_max=21., energy_spectrum='power_law', gamma=-3) sim.set_charges('mixed') xmax = sim.set_xmax() sim.set_sources(10) sim.set_rigidity_bins(np.arange(17., 20.5, 0.02)) sim.smear_sources(delta=0.1, dynamic=True) sim.arrival_setup(1.) vecs = hpt.pix2vec(sim.nside, np.hstack(sim.crs['pixel'])) sim.apply_uncertainties(err_e=0.1, err_a=1, err_xmax=10) # check that array are not equal but deviations are smaller than 5 sigma self.assertTrue(not (log10e == sim.crs['log10e']).all()) self.assertTrue((np.abs(10**(log10e - 18) - 10**(sim.crs['log10e'] - 18)) < 5*0.1*10**(log10e - 18)).all()) self.assertTrue(not (xmax == sim.crs['xmax']).all()) self.assertTrue((np.abs(xmax - sim.crs['xmax']) < 50).all()) vec_unc = coord.ang2vec(np.hstack(sim.crs['lon']), np.hstack(sim.crs['lat'])) self.assertTrue(not (coord.angle(vecs, vec_unc) == 0).all()) self.assertTrue((coord.angle(vecs, vec_unc) < np.deg2rad(10)).all())
def sensitivity_2pt(self, niso=1000, bins=180, **kwargs): """ Function to calculate the sensitivity by the 2pt-auto-correlation over a scrambling of the right ascension coordinates. :param niso: Number of isotropic sets to calculate. :param bins: Number of angular bins, 180 correspond to 1 degree binning (np.linspace(0, np.pi, bins+1). :param kwargs: additional named arguments passed to obs.two_pt_auto() :return: pvalues in the shape (bins) """ kwargs.setdefault('cumulative', True) vec_crs = self.get('vecs') _, dec = coord.vec2ang(coord.gal2eq(vec_crs)) # calculate auto correlation for isotropic scrambled data _ac_iso = np.zeros((niso, bins)) for i in range(niso): _vecs = coord.ang2vec(coord.rand_phi(self.ncrs), dec) _ac_iso[i] = obs.two_pt_auto(_vecs, bins, **kwargs) # calculate p-value by comparing the true sets with the isotropic ones _ac_crs = obs.two_pt_auto(vec_crs, bins, **kwargs) pvals = np.sum(_ac_iso >= _ac_crs[np.newaxis], axis=0) / float(niso) return pvals
import numpy as np import matplotlib.pyplot as plt from astrotools import auger, coord, skymap print("Test: module coord.py") # Creates an isotropic arrival map and convert galactic longitudes (lons) and # galactic latitudes (lats) into cartesian vectors ncrs = 3000 # number of cosmic rays log10e_min = 18.5 # minimum energy in log10(E / eV) lons = coord.rand_phi(ncrs) # isotropic in phi (~Uniform(-pi, pi)) lats = coord.rand_theta(ncrs) # isotropic in theta (Uniform in cos(theta)) vecs = coord.ang2vec(lons, lats) log10e = auger.rand_energy_from_auger(n=ncrs, log10e_min=log10e_min) # Plot an example map with sampled energies. If you specify the opath keyword in # the skymap function, the plot will be automatically saved and closed skymap.scatter(vecs, c=log10e, opath='isotropic_sky.png') # Creates an arrival map with a source located at v_src=(1, 0, 0) and apply a # fisher distribution around it with gaussian spread sigma=10 degree v_src = np.array([1, 0, 0]) kappa = 1. / np.radians(10.)**2 vecs = coord.rand_fisher_vec(v_src, kappa=kappa, n=ncrs) # if you dont specify the opath you can use (fig, ax) to plot more stuff fig, ax = skymap.scatter(vecs, c=log10e) plt.scatter(0, 0, s=100, c='red', marker='*') # plot source in the center plt.savefig('fisher_single_source_10deg.png', bbox_inches='tight') plt.close() # We can also use the coord.rand_fisher_vec() function to apply an angular uncertainty
# If you just have a single cosmic ray set you want to use the ComicRaysBase. You can # set arbitrary content in the container. Objects with shape (self.crs) will be # stored in an internal array called 'shape_array', all other data in a # dictionary called 'general_object_store'. nside = 64 npix = hpt.nside2npix(nside) ncrs = 5000 exposure = hpt.exposure_pdf(nside) lon, lat = hpt.pix2ang(nside, hpt.rand_pix_from_map(exposure, n=ncrs)) crs = cosmic_rays.CosmicRaysBase(ncrs) # Initialize cosmic ray container # you can set arbitrary content in the container. Objects with different shape # than (ncrs) will be stored in an internal dictionary called 'general_object_store' crs['lon'], crs['lat'] = lon, lat crs['date'] = 'today' crs['log10e'] = auger.rand_energy_from_auger(log10e_min=19, n=ncrs) crs.set('vecs', coord.ang2vec(lon, lat)) # another possibility to set content crs.keys() # will print the keys that are existing # Save, load and plot cosmic ray base container opath = 'cr_base_container.npz' crs.save(opath) crs_load = cosmic_rays.CosmicRaysBase(opath) crs_load.plot_heatmap(opath='cr_base_healpy.png') crs_load.plot_eventmap(opath='cr_base_eventmap.png') # You can also quickly write all data in an usual ASCII file: crs.save_readable('cr_base.txt') # For a big simulation with a lot of sets (simulated skys), you should use the # CosmicRaysSets(). Inheriting from CosmicRaysBase(), objects with different
plt.ylabel('E$^3$ dN', fontsize=16) plt.savefig('energy_spectrum.png') plt.clf() ######################################## # Module: coord.py ######################################## print("Test: module coord.py") # Creates an isotropic arrival map and convert galactic longitudes (lons) and # galactic latitudes (lats) into cartesian vectors ncrs = 3000 # number of cosmic rays lons = coord.rand_phi(ncrs) # isotropic in phi (~Uniform(-pi, pi)) lats = coord.rand_theta(ncrs) # isotropic in theta (Uniform in cos(theta)) vecs = coord.ang2vec(lons, lats) log10e = auger.rand_energy_from_auger(n=ncrs, log10e_min=emin) # Plot an example map with sampled energies. If you specify the opath keyword in # the skymap function, the plot will be automatically saved and closed skymap.scatter(vecs, c=log10e, opath='isotropic_sky.png') # Creates an arrival map with a source located at v_src=(1, 0, 0) and apply a # fisher distribution around it with gaussian spread sigma=10 degree v_src = np.array([1, 0, 0]) kappa = 1. / np.radians(10.)**2 vecs = coord.rand_fisher_vec(v_src, kappa=kappa, n=ncrs) # if you dont specify the opath you can use (fig, ax) to plot more stuff fig, ax = skymap.scatter(vecs, c=log10e) plt.scatter(0, 0, s=100, c='red', marker='*') # plot source in the center plt.savefig('fisher_single_source_10deg.png', bbox_inches='tight') plt.close()
def scatter(v, c=None, cblabel='log$_{10}$(Energy / eV)', opath=None, fig=None, **kwargs): """ Scatter plot of events with arrival directions x,y,z and colorcoded energies. :param v: array of shape (3, n) pointing into directions of the events :param c: quantity that is supposed to occur in colorbar, e.g. energy of the cosmic rays :param cblabel: colorbar label :param opath: if not None, saves the figure to the given opath (no returns) :param fig: figure to plot in, creates new figure if None :param kwargs: additional named keyword arguments - figsize: figure size as input for plt.figure() - cmap: colormap - cbar: if True includes a colobar - cticks: sets ticks of colormap - mask_alpha: alpha value for maskcolor - fontsize: scale the general fontsize - dark_grid: if True paints a dark grid (useful for bright maps) - gridcolor: Color of the grid. - gridalpha: Transparency value of the gridcolor. - tickcolor: Color of the ticks. - tickalpha: Transparency of the longitude ticks. - plane: plots 'SGP' or 'GP' or both (list) into plot - planecolor: color of plane - coord_system: default galactic ('gal') / equatorial ('eq') :return: figure, axis of the scatter plot """ lons, lats = coord.vec2ang(v) fontsize = kwargs.pop('fontsize', 26) kwargs.setdefault('s', 8) if 'marker' not in kwargs: kwargs.setdefault('lw', 0) cbar = kwargs.pop('cbar', True) and isinstance(c, (list, tuple, np.ndarray)) if cbar: vmin = kwargs.pop( 'vmin', smart_round(np.min(c[np.isfinite(c)]), upper_border=False)) vmax = kwargs.pop( 'vmax', smart_round(np.max(c[np.isfinite(c)]), upper_border=True)) step = smart_round((vmax - vmin) / 5., order=1) cticks = kwargs.pop( 'cticks', np.round(np.arange(vmin, vmax, step), int(np.round(-np.log10(step), 0)))) clabels = kwargs.pop('clabels', cticks) # read keyword arguments for the grid dark_grid = kwargs.pop('dark_grid', True) gridcolor = kwargs.pop('gridcolor', 'lightgray' if dark_grid is None else 'black') gridalpha = kwargs.pop('gridalpha', 0.5 if dark_grid is None else 0.4) tickcolor = kwargs.pop('tickcolor', 'lightgray' if dark_grid is None else 'black') tickalpha = kwargs.pop('tickalpha', 0.5 if dark_grid is None else 1) planecolor = kwargs.pop('planecolor', 'darkgray') plane = kwargs.pop('plane', None) coord_system = kwargs.pop('coord_system', 'gal') if coord_system == 'eq': lons, lats = coord.vec2ang(coord.gal2eq(coord.ang2vec(lons, lats))) # mimic astronomy convention: positive longitudes evolving to the left with respect to GC lons = -lons # plot the events fig = plt.figure( figsize=kwargs.pop('figsize', [12, 6])) if fig is None else fig ax = fig.add_axes([0.1, 0.1, 0.85, 0.9], projection="hammer") events = ax.scatter(lons, lats, c=c, **kwargs) if cbar: cbar = plt.colorbar(events, orientation='horizontal', shrink=0.85, pad=0.05, aspect=30, ticks=cticks) cbar.set_label(cblabel, fontsize=fontsize) events.set_clim(vmin, vmax) cbar.ax.tick_params(labelsize=fontsize - 4) cbar.set_ticklabels(clabels) cbar.draw_all() # Setup the grid plt.xticks(fontsize=fontsize) plt.yticks(fontsize=fontsize) plot_grid(gridcolor=gridcolor, gridalpha=gridalpha, tickalpha=tickalpha, tickcolor=tickcolor, fontsize=fontsize) if plane is not None: plot_plane(planecolor, coord_system, plane) if opath is not None: plt.savefig(opath, bbox_inches='tight') plt.clf() return fig, ax