-
Notifications
You must be signed in to change notification settings - Fork 0
/
ric_tools.py
1788 lines (1593 loc) · 65.1 KB
/
ric_tools.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
import numpy as np
import copy
#from .tools import mask_good, mask_bad
import subprocess as sp
import scipy
from scipy import stats
import inspect
import numpy as np
import healpy as hp
import os
from astropy.table import Table
import tempfile as tf
import shutil
from spt3g import core, util, mapspectra, maps, std_processing
from spt3g.mapspectra import curved_sky as cs
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
from spt3g.util import healpix_tools as hpt
def average_N_spectra(spectra, N_spectra, N_ells):
avgSpectra = np.zeros(N_ells)
rmsSpectra = np.zeros(N_ells)
# calcuate the average spectrum
i = 0
while (i < N_spectra):
avgSpectra = avgSpectra + spectra[i, :]
i = i + 1
avgSpectra = avgSpectra/(1. * N_spectra)
# calculate the rms of the spectrum
i = 0
while (i < N_spectra):
rmsSpectra = rmsSpectra + (spectra[i, :] - avgSpectra)**2
i = i + 1
rmsSpectra = np.sqrt(rmsSpectra/(1.*N_spectra))
#rmsSpectra = np.std(spectra, axis=0)
return(avgSpectra, rmsSpectra)
def bin_spectrum(cls, lmin=8, lmax=None, binwidth=25, return_error=False):
cls = np.atleast_2d(cls)
if lmax is None:
lmax = cls.shape[-1] - 1
ell = np.arange(lmax + 1)
bins = np.arange(lmin, lmax + 1, binwidth)
ellb = stats.binned_statistic(ell, ell, statistic=np.mean, bins=bins)[0]
clsb = np.array([stats.binned_statistic(
ell, C, statistic=np.mean, bins=bins)[0] for C in cls]).squeeze()
if return_error:
clse = np.array([stats.binned_statistic(
ell, C, statistic=np.std, bins=bins)[0] for C in cls]).squeeze()
return ellb, clsb, clse
return ellb, clsb
def extract_func_kwargs(func, kwargs, pop=False, others_ok=True, warn=False):
"""
Extract arguments for a given function from a kwargs dictionary
Arguments
---------
func : function or callable
This function's keyword arguments will be extracted.
kwargs : dict
Dictionary of keyword arguments from which to extract.
NOTE: pass the kwargs dict itself, not **kwargs
pop : bool, optional
Whether to pop matching arguments from kwargs.
others_ok : bool
If False, an exception will be raised when kwargs contains keys
that are not keyword arguments of func.
warn : bool
If True, a warning is issued when kwargs contains keys that are not
keyword arguments of func. Use with `others_ok=True`.
Returns
-------
Dict of items from kwargs for which func has matching keyword arguments
"""
spec = inspect.getargspec(func)
func_args = set(spec.args[-len(spec.defaults):])
ret = {}
for k in kwargs.keys():
if k in func_args:
if pop:
ret[k] = kwargs.pop(k)
else:
ret[k] = kwargs.get(k)
elif not others_ok:
msg = "Found invalid keyword argument: {}".format(k)
raise TypeError(msg)
if warn and kwargs:
s = ', '.join(kwargs.keys())
warn("Ignoring invalid keyword arguments: {}".format(s), Warning)
return ret
def projsetup(ax=None, bm=None, projection='laea', region='spt3g', coord='C',
lat_0=None, lon_0=None, width=None, height=None, flip='astro',
bgcolor='0.85', graticule=True, dpar=None, dmer=None, loc_par=None,
loc_mer=None, nsew=None, grat_opts=None, xlabel=None, ylabel=None,
labelpad=None, xlabelpad=None, ylabelpad=None, xlabelpos=None,
ylabelpos=None, graticule_rot=False, title='', text=None,
text_props=None):
"""
Create a Basemap instance and setup axes for plotting maps, including
graticules, rotated graticules, axis labels and title text.
Arguments
---------
ax : matplotlib.Axes instance
If None, this is assumed to be `plt.gca()`.
bm : Basemap instance
If None, this is created using the remaining parameters, and the
axes instance is populated with graticules.
If not None, the basemap instance and axes are returned without changes
to graticules or axis labels.
projection : string, optional
A projection name supported by basemap. Default: equal area azimuthal
coord : sequence of character
Either one of 'G', 'E' or 'C' to describe the coordinate
system of the map, or a sequence of 2 of these to rotate
the map from the first to the second coordinate system.
lat_0, lon_0 : float, optional
Coordinates of map center point. Default based on region and projection.
width, height : float, optional
Projection plane size. Units are degree-like.
Default based on region and projection.
flip : {'astro', 'geo'}, optional
Defines the convention of projection : 'astro' (east towards left,
west towards right) or 'geo' (east towards right, west towards left)
bgcolor : matplotlib color, optional
Background colour of the image
graticule : string, boolean, tuple, or dict, optional
Draw a graticule in a given coordinate system. Defaults to `coord`.
Tuple or dict behave like args to hp.graticule.
If graticule is in different coordinates than the map, options are
passed to `projgrat_rot` instead.
dpar, dmer : float, optional
Spacing of graticule parallels and meridians (degrees).
Default based on region and projection.
loc_par, loc_mer : 4-element list of boolean, optional
Positions of graticule labels. [left, right, top, bottom]
Default based on region and projection.
grat_opts : dictionary, optional
Dictionary of extra arguments for drawing graticules (linewidth, etc)
nsew : bool, optional
Whether graticule labels should use "N/S/E/W" or "+/-"
xlabel, ylabel : string, optional
Default based on coordinate system. Use empty string '' to turn off.
labelpad : int, optional
axis padding of graticule labels. If None, use `xtick.major.pad`
and `ytick.major.pad` from rcParams. Override with `xoffeset`
or `yoffset` options to `grat_opts`.
xlabelpad, ylabelpad : int, optional
Padding of axis labels. If None, use `axes.labelpad` from rcParams.
xlabelpos, ylabelpos : str, optional
Position of the axis labels: 'left', 'right', 'top' or 'bottom'
graticule_rot : bool or dict, optional
If True, draw rotated graticules in the alternate coordinate system
(in G if map is in C, and vice versa). If dict, pass arguments
to `projgrat_rot`.
title : str, optional
The title of the plot. Default: ''
text : 4 element list of str, optional
Text to write on top of the figure in [top left, top right,
bottom right, bottom left]. Can leave empty strings for positions
with no text.
text_props : dict or 4 element list of dicts, optional
Standard text properties to apply to the text written on the figure.
Can have different properties for the four different text locations
if text_props is a list of dicts, or uniformly apply text_props to
all locations if a dict.
Returns
-------
ax : matplotlib.Axes instance
The axes on which the data are to be plotted
bm : Basemap instance
The Basemap plotting object for the requested projection parameters
"""
# deal with coord for region definition
if len(coord) == 2:
coord_region = coord[1]
elif len(coord) == 1:
coord_region = coord
else:
raise ValueError("Invalid 'coord' argument")
if coord_region not in ['C', 'G']:
raise ValueError("Invalid 'coord' argument")
# select axes labels
xlabeldict = {'G': 'Galactic Longitude', 'C': 'Right Ascension'}
ylabeldict = {'G': 'Galactic Latitude', 'C': 'Declination'}
# graticule options
graticule = coord_region if graticule is True else graticule
if isinstance(graticule, tuple):
dpar = graticule[0]
dmer = graticule[1]
graticule = graticule[2]
elif isinstance(graticule, dict):
graticule = graticule.copy()
dpar = graticule.pop("dpar", None)
dmer = graticule.pop("dmer", None)
grat_opts = graticule if grat_opts is None else grat_opts
graticule = graticule.pop("coord", coord_region)
if grat_opts is None:
grat_opts = {}
else:
grat_opts = grat_opts.copy()
if projection == 'moll':
region = None
width = None
height = None
dpar = 30 if dpar is None else dpar
dmer = 30 if dmer is None else dmer
loc_par = [0, 0, 0, 0] if loc_par is None else loc_par
loc_mer = [0, 0, 0, 0] if loc_mer is None else loc_mer
lon_0 = 0 if lon_0 is None else lon_0
elif projection == 'laea':
loc_par = [1, 0, 0, 1] if loc_par is None else loc_par
loc_mer = [0, 0, 1, 0] if loc_mer is None else loc_mer
xlabel = xlabeldict[coord_region] if xlabel is None else xlabel
ylabel = ylabeldict[coord_region] if ylabel is None else ylabel
xlabelpos = 'top' if xlabelpos is None else xlabelpos
ylabelpos = 'left' if ylabelpos is None else ylabelpos
xlabelpad = 25 if xlabelpad is None else xlabelpad
ylabelpad = 30 if ylabelpad is None else ylabelpad
dpar = 15 if dpar is None else dpar
dmer = 20 if dmer is None else dmer
if region in ['spt3g','spt3g_summer']:
if coord_region == 'C':
lat_0 = -60 if lat_0 is None else lat_0
lon_0 = 0 if lon_0 is None else lon_0
width = 80 if width is None else width
height = 40 if height is None else height
elif coord_region == 'G':
lat_0 = -75 if lat_0 is None else lat_0
lon_0 = -60 if lon_0 is None else lon_0
width = 120 if width is None else width
height = 40 if height is None else height
elif region == 'cmb_mask':
if coord_region == 'C':
lat_0 = -37 if lat_0 is None else lat_0
lon_0 = 52 if lon_0 is None else lon_0
width = 72 if width is None else width
height = 48 if height is None else height
elif coord_region == 'G':
lat_0 = -56 if lat_0 is None else lat_0
lon_0 = -118 if lon_0 is None else lon_0
width = 50 if width is None else width
height = 72 if height is None else height
elif region is not None:
raise ValueError("Invalid region: {} for {}".format(
region, projection))
else:
warn("Guessing defaults for projection {}. May need to set"
" lat_0, lon_0, width, height, etc")
lat_0 = -50 if lat_0 is None else lat_0
lon_0 = 65 if lon_0 is None else lon_0
width = 135 if width is None else width
height = 90 if height is None else height
dpar = 30 if dpar is None else dpar
dmer = 30 if dmer is None else dmer
# rotated graticule options
if graticule and graticule != coord_region:
graticule = False
graticule_rot = grat_opts
if dpar is not None:
graticule_rot.setdefault('dpar', dpar)
if dmer is not None:
graticule_rot.setdefault('dmer', dmer)
if isinstance(graticule_rot, dict):
grat_opts_rot = graticule_rot
graticule_rot = True
else:
grat_opts_rot = {}
if projection == 'moll' and graticule_rot:
# buggy
NotImplementedError(
"Different grat coords not implemented for mollweide projection")
if graticule:
style = "+/-" if not nsew else ""
grat_opts.setdefault('dpar', dpar)
grat_opts.setdefault('dmer', dmer)
grat_opts.setdefault('loc_par', loc_par)
grat_opts.setdefault('loc_mer', loc_mer)
grat_opts.setdefault('flip', flip)
grat_opts.setdefault('fontsize', 'medium')
grat_opts.setdefault('labelstyle', style)
grat_opts.setdefault('labelpad', labelpad)
grat_opts.setdefault('label_par', ylabel)
grat_opts.setdefault('label_mer', xlabel)
grat_opts.setdefault('labelpos_par', ylabelpos)
grat_opts.setdefault('labelpos_mer', xlabelpos)
grat_opts.setdefault('labelpad_par', ylabelpad)
grat_opts.setdefault('labelpad_mer', xlabelpad)
if graticule_rot:
grat_opts_rot.setdefault(
'coord', ['G' if coord_region == 'C' else 'C', coord_region])
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
if ax is None:
ax = plt.gca()
# create projection and set up axes
if bm is None:
bm = Basemap(projection=projection, rsphere=180/np.pi, lat_0=lat_0,
lon_0=lon_0, width=width, height=height, resolution=None)
# setup axes
bm.set_axes_limits(ax=ax)
if flip == "astro":
xl = ax.get_xlim()
if xl[0] < xl[1]:
# fix and invert axis limits
ax.set_xlim(xl[1], xl[0])
if bgcolor:
ax.set_facecolor(bgcolor)
# draw graticules
if graticule:
projgrat(bm, ax=ax, **grat_opts)
if graticule_rot:
projgrat_rot(bm, ax=ax, **grat_opts_rot)
# store view configuration
bm.region = region
bm.coord = coord_region
# figure text
if title:
plt.suptitle(title)
if text_props is None:
text_props = {}
if text is not None:
if isinstance(text, str):
text = [text, "", "", ""]
textlocs = {0: ['top', 'left', 0.02, .98],
1: ['top', 'right', 0.98, 0.98],
2: ['bottom', 'right', 0.98, 0.02],
3: ['bottom', 'left', 0.02, 0.02]}
if isinstance(text_props, dict):
text_props = text_props.copy()
text_props = np.tile(text_props, 4)
else:
for i in xrange(len(text_props)):
text_props[i] = text_props[i].copy()
for l in range(4):
text_props[l].setdefault('zorder', 5)
ax.text(textlocs[l][2], textlocs[l][3], text[l],
verticalalignment=textlocs[l][0],
horizontalalignment=textlocs[l][1],
transform=ax.transAxes, **text_props[l])
# return
return ax, bm
def projview(m, vmin=None, vmax=None, pmin=0.5, pmax=99.5, coord='C',
resol=None, diverging=True, cmap=None, log=False, cbar=True, unit=None,
cbar_extend='neither', cbar_ticks=None, cbar_minorticks=False,
nest=False, return_projected_map=False, interp=True, pad=0.0, **kwargs):
"""
Plot an healpix map (given as an array) in arbitrary projection.
Uses mpl_toolkits.basemap to project map and graticules.
Options are similar to healpy.cartview, with a few adjustments for
SPT3G-specific plotting (e.g. the 'region' option) and projections.
Parameters
----------
map : float, array-like or None
An array containing the map,
supports masked maps, see the `ma` function.
If None, will display a blank map, useful for overplotting.
pmin, pmax : float, optional
Percentile minimum and maximum range values. Can also provide as a
string with "%" to (v)min or (v)max.
min, max, vmin, vmax : float, optional
The minimum and maximum range values. Override pmin, pmax.
v-less forms for compatibility with healpy.
coord : sequence of character
Either one of 'G', 'E' or 'C' to describe the coordinate
system of the map, or a sequence of 2 of these to rotate
the map from the first to the second coordinate system.
If the coordinate system of the map differs from that of the
configured `Basemap`, the map coordinates will be rotated.
resol : float, optional
Resolution of the image in pixels per projected "degree". Image will
have dimenstion (width * resol) by (height * resol).
diverging : bool, optional
Whether to set symmetric colour limits for diverging colour scale.
Default True.
cmap : string or colormap, optional
The colormap to use. Default chosen based on region
log : bool, optional
norm : `matplotlib.colors.Normalize` instance
If `log` is True, use a logarithmic colour scale, otherwise linear.
For other general normalizations use the norm kwarg, which will be
passed to imshow (and override this option)
cbar : bool or string, optional
Display the colorbar. String specifies position ('left', 'right',
'bottom', 'top'). Will interpret 'vertical' as 'right' or 'horizontal'
as 'bottom'. Default: 'right' when set to True.
unit : str, optional
A text describing the unit of the data.
cbar_extend : string, optional
Set to "min", "max", or "both" to show extended colour for out-of-range
cbar_ticks : list, optional
Colorbar tick positions.
cbar_minorticks : bool, optional
If True, turn on minor ticks on colorbar axis.
nest : bool, optional
If True, ordering scheme is NESTED. Default: False (RING)
return_projected_map : bool, optional
if True returns the projected map in a 2d numpy array
interp : bool, optional
If True, interpolate values using bilinear interpolation. If False,
use the nearest neighbor value.
Keyword Arguments:
------------------
Remaining arguments are passed to `projsetup` to create the Basemap
instance and populate the figure axes with labels and graticules.
Notably:
bm : Basemap instance
A pre-configured Basemap instance
projection : string, optional
A projection name supported by basemap. Default: equal area azimuthal
region : string, optional
Determine projection parameters automatically based on the desired
region to plot. Options are 'spider' (SPIDER CMB region aka 'cmb')
and 'rcw38'. Can specify 'both' for CMB + RCW38.
coord : sequence of character
Either one of 'G', 'E' or 'C' to describe the coordinate
system of the map, or a sequence of 2 of these to rotate
the map from the first to the second coordinate system.
Any further remaining arguments are passed to imshow. Notably:
ax : matplotlib Axes instance
The axes in which to plot. For use with, eg, pyplot.subplots
Returns
-------
mxy : array_like, optional
Returned if return_projected_map is True
im : matplotlib.image.AxesImage instance
The image object
bm : basemap.Basemap
The basemap object. For extra plotting functions in map coords
cbar : matplotlib.colorbar.Colorbar instance, optional
The colorbar object, if cbar is set
"""
import matplotlib.pyplot as plt
setup_opts = extract_func_kwargs(
projsetup, kwargs, pop=True, others_ok=True)
ax, bm = projsetup(coord=coord, **setup_opts)
projection = bm.projection
region = bm.region
if list(coord)[-1] != bm.coord:
coord = [list(coord)[0], bm.coord]
if cmap is None:
cmap = plt.cm.RdBu_r if diverging else "inferno"
# colorbar options
if cbar is True or cbar in ['v', 'vertical', 'r']:
cbar = 'right'
elif cbar in ['h', 'horizontal', 'b','bottom']:
cbar = 'bottom'
pad = 0.4
cbar_label = unit if unit is not None else ""
# color scale
vmin = kwargs.pop('min', None) if vmin is None else vmin
vmax = kwargs.pop('max', None) if vmax is None else vmax
if isinstance(vmin, str) and '%' in vmin:
pmin = float(vmin.rstrip('%'))
if isinstance(vmax, str) and '%' in vmax:
pmax = float(vmax.rstrip('%'))
if pmin is not None or pmax is not None:
mask = mask_good(m, badinf=True)
if vmin is None:
vmin = np.percentile(m[mask], pmin)
if vmax is None:
vmax = np.percentile(m[mask], pmax)
if diverging:
val = np.max([np.abs(vmin), np.abs(vmax)])
vmin = -val
vmax = val
if kwargs.get("norm", None) == "log":
kwargs.pop("norm")
log = True
if log:
from matplotlib.colors import LogNorm
kwargs.setdefault("norm", LogNorm(vmin=vmin, vmax=vmax))
# convert UNSEEN values to NAN (temorarily)
m = m.copy()
unseen_mask = (m == hp.UNSEEN)
m[unseen_mask] = np.nan
if resol is None:
resol = 5 if projection == 'moll' else 15
nx = int((bm.xmax - bm.xmin) * resol)
ny = int((bm.ymax - bm.ymin) * resol)
phi, theta = bm.makegrid(nx, ny)
# convert lat/long from makegrid to theta/phi in radians
theta = np.deg2rad(90 - theta)
phi = np.deg2rad(phi)
# rotate pixel angles if converting coordinates
if len(coord) == 2:
# note that I need rotation for opposite direction from coord
rmat = hp.rotator.get_coordconv_matrix((coord[1], coord[0]))[0]
theta, phi = hp.rotator.rotateDirection(
rmat, theta.ravel(), phi.ravel())
theta = theta.reshape((ny, nx))
phi = phi.reshape((ny, nx))
# get values at each projected pixel. Either bilinear or nearest neighbour
if interp:
dat = hp.get_interp_val(m, theta, phi, nest=nest)
else:
dat = m[hp.ang2pix(hp.npix2nside(len(m)), theta, phi, nest=nest)]
im = bm.imshow(dat, vmin=vmin, vmax=vmax, origin="lower", cmap=cmap,
ax=ax, **kwargs)
if projection == 'moll':
limb = bm.drawmapboundary(fill_color='white', ax=ax)
im.set_clip_path(limb)
def fmt(x, pos):
a, b = '{:.2e}'.format(x).split('e')
b = int(b)
return r'${} \times 10^{{{}}}$'.format(a, b)
if cbar:
cb = bm.colorbar(im, location=cbar, label=cbar_label,
extend=cbar_extend, pad=pad, ax=ax, format='%.0e')
if cbar_ticks is not None:
cb.set_ticks(cbar_ticks)
if cbar_minorticks:
cb.ax.minorticks_on()
plt.sca(ax) # reset current axis to main image
else:
cb = None
# restore UNSEEN pixels
m[unseen_mask] = hp.UNSEEN
plt.draw()
# return stuff
return (dat,) * return_projected_map + (im, bm) + (cb,) * bool(cbar)
def projgrat(bmap, dpar=15, dmer=20, loc_par=[1, 0, 0, 1], loc_mer=[0, 0, 1, 0],
label_par=None, label_mer=None, labelpad=None, flip='astro',
zorder=5, ax=None, **kwargs):
'''Draw lat-lon graticules.
In Basemap, the label pad is computed in projection units. Now you can use
the keyword argument 'labelpad' to control this separation in points. If
not specified then this value is taken from rcParams.
Arguments:
bmap : Basemap object
dpar, dmer : int
Difference in degrees from one longitude or latitude to the next.
loc_par, loc_mer : 4-tuple of bools
list of 4 values (default [0,0,0,0]) that control
whether parallels are labelled where they intersect
the left, right, top or bottom of the plot. For
example labels=[1,0,0,1] will cause parallels
to be labelled where they intersect the left and
and bottom of the plot, but not the right and top
label_par, label_mer : string, optional
Parallel and meridian axis labels.
labelpad : int, optional
axis padding of graticule labels. If None, use `xtick.major.pad`
and `ytick.major.pad` from rcParams.
labelpad_par, labelpad_mer : int, optional
axis padding of axis labels. If None, use `axes.labelpad` from
rcParams.
labelpos_par, labelpos_mer : string, optional
location of axis labels. 'left', 'right', 'top' or 'bottom'.
if not supplied, these are automatically determined based on `loc_par`
and `loc_mer`.
flip : {'astro', 'geo'}, optional
Defines the convention of projection : 'astro' (east towards left,
west towards right) or 'geo' (east towards right, west towards left)
ax : matplotlib Axes instance
Axes in which to draw graticules. Current axes by default.
**kwargs -- Other arguments to drawparallels, drawmeridians and plt.text.
'''
# Processes arguments and rcParams for default values
import matplotlib.pyplot as plt
# kwargs.setdefault('color', plt.rcParams['grid.color'])
# kwargs.setdefault('linewidth', plt.rcParams['grid.linewidth'])
if ax is None:
ax = plt.gca()
if labelpad is not None:
padx = pady = labelpad
else:
pady = plt.rcParams['xtick.major.pad']
padx = plt.rcParams['ytick.major.pad']
labelsize = kwargs.pop('fontsize', None)
if labelsize is not None:
xfontsize = yfontsize = labelsize
else:
xfontsize = plt.rcParams['xtick.labelsize']
yfontsize = plt.rcParams['ytick.labelsize']
labelpad_par = kwargs.pop('labelpad_par', None)
if labelpad_par is None:
labelpad_par = plt.rcParams['axes.labelpad']
labelpad_mer = kwargs.pop('labelpad_mer', None)
if labelpad_mer is None:
labelpad_mer = plt.rcParams['axes.labelpad']
labelpos_par = kwargs.pop('labelpos_par', None)
labelpos_mer = kwargs.pop('labelpos_mer', None)
# Vectors of coordinates
parallels = np.arange(0., 90., dpar)
parallels = np.sort(np.concatenate((parallels, -parallels[1:])))
meridians = np.arange(0., 180., dmer)
meridians = np.sort(np.concatenate((meridians, -meridians[1:])))
kwargs.setdefault(
'latmax', parallels[-1] if parallels[-1] <= 85 else parallels[-2])
if loc_par is None:
loc_par = [0] * 4
if loc_mer is None:
loc_mer = [0] * 4
if labelpos_par is None:
labelpos_par = ('left' if loc_par[0] else 'right' if loc_par[1]
else 'bottom' if loc_par[3] else 'top' if loc_par[2]
else 'left')
if labelpos_mer is None:
labelpos_mer = ('bottom' if loc_mer[3] else 'top' if loc_mer[2]
else 'left' if loc_mer[0] else 'right' if loc_mer[1]
else 'bottom')
if flip == "astro":
# also need to flip graticule labels
loc_par[0], loc_par[1] = loc_par[1], loc_par[0]
loc_mer[0], loc_mer[1] = loc_mer[1], loc_mer[0]
# If not specified then compute the label offset by 'labelpad'
xos = kwargs.pop('xoffset', None)
yos = kwargs.pop('yoffset', None)
if xos is None and yos is None:
# Page size in inches and axes limits
fig_w, fig_h = plt.gcf().get_size_inches()
(x1, y1), (x2, y2) = plt.gca().get_position().get_points()
# Width and height of axes in points
w = (x2 - x1) * fig_w * 72
h = (y2 - y1) * fig_h * 72
# If the aspect relation is fixed then compute the real values
if bmap.fix_aspect:
aspect = bmap.aspect * w / h
if aspect > 1:
w = h / bmap.aspect
elif aspect < 1:
h = w * bmap.aspect
# Offset in projection units (meters or degrees)
xos = padx * (bmap.urcrnrx - bmap.llcrnrx) / w
yos = pady * (bmap.urcrnry - bmap.llcrnry) / h
# Draws the grid
retpar = bmap.drawparallels(parallels, labels=loc_par, fontsize=yfontsize,
xoffset=xos, yoffset=yos, ax=ax, **kwargs)
if flip == 'astro':
for ll, tt in retpar.values():
for t in tt:
if t.get_ha() == 'left':
t.set_ha('right')
elif t.get_ha() == 'right':
t.set_ha('left')
bmap.parlabels = retpar
retmer = bmap.drawmeridians(meridians, labels=loc_mer, fontsize=xfontsize,
xoffset=xos, yoffset=yos, ax=ax, **kwargs)
if flip == 'astro':
for ll, tt in retmer.values():
for t in tt:
if t.get_ha() == 'left':
t.set_ha('right')
elif t.get_ha() == 'right':
t.set_ha('left')
bmap.merlabels = retmer
def draw_label(label, labelpos, labelpad):
if labelpos in ['left', 'right']:
ax.set_ylabel(label, labelpad=labelpad)
ax.yaxis.set_label_position(labelpos)
else:
ax.set_xlabel(label, labelpad=labelpad)
ax.xaxis.set_label_position(labelpos)
if label_par:
draw_label(label_par, labelpos_par, labelpad_par)
if label_mer:
draw_label(label_mer, labelpos_mer, labelpad_mer)
def mask_good(m, badval=hp.UNSEEN, rtol=1.e-5, atol=1.e-8,
badnan=True, badinf=True):
"""Returns a bool array with ``False`` where m is close to badval,
NaN or inf.
Parameters
----------
m : a map (may be a sequence of maps)
badval : float, optional
The value of the pixel considered as bad (:const:`UNSEEN` by default)
rtol : float, optional
The relative tolerance
atol : float, optional
The absolute tolerance
badnan : bool, optional
If True, also mask NaN values
badinf : bool, optional
If True, also mask inf values
Returns
-------
a bool array with the same shape as the input map, ``False`` where input map is
close to badval, NaN or inf, and ``True`` elsewhere.
See Also
--------
mask_bad, ma
Examples
--------
>>> import healpy as hp
>>> m = np.arange(12.)
>>> m[3] = hp.UNSEEN
>>> m[4] = np.nan
>>> mask_good(m)
array([ True, True, True, False, False, True, True, True, True,
True, True, True], dtype=bool)
"""
m = np.asarray(m)
mask = np.ones_like(m, dtype=bool)
if badnan:
mask &= ~np.isnan(m)
if badinf:
mask &= np.isfinite(m)
mask[mask] = hp.mask_good(m[mask], badval=badval, rtol=rtol, atol=atol)
return mask
def fit_foreground_template(m, template, p0, mask=None, joint_fit=True,
smooth=False, fwhm=.5, in_place=False):
'''
Fits the template to the input map by minimizing the variance in the
cleaned map
Args
----------
m : float array
The input map.
template : list of float_array
The foreground templates. It should be leakage subtracted.
p0 : list of float
The initial guess for the fit.
Optional Args
-------------
mask : bool array
The region to mask. Default None.
joint_fit : bool
Fit I and P at the same time. Default True.
smooth : bool
Presmooth the map before fitting. Smooths both m and template. Default
is False
fwhm : float
If smooth is True, then smooth with a beam with full-width half max
fwhm in arcmin. Default is 1.2, this is the one of SPT3G 150GHz.
in_place : bool
If True, does not create a copy of the data to perform operations. This
will alter the input maps.
Returns
-------
fit_amp : float
Returns the fit parameter. If joint_fit is False, returns two fit
values, the first for I and the second for P. If the fit fails,
returns np.nan
'''
m = np.array([np.asarray(m['T']), np.asarray(m['Q']), np.asarray(m['U'])])
template = np.array([np.asarray(template['T']), np.asarray(template['Q']), np.asarray(template['U'])])
if mask is not None:
if not isinstance(mask[1], np.bool_):
mask = ~mask.astype(bool)
else:
mask = ~mask
m[..., mask] = np.nan
if smooth:
m = hpt.smoothing(m, fwhm=np.deg2rad(fwhm))
# Apply mask
if mask is not None:
template[..., mask] = np.nan
if smooth:
template[:] = hpt.smoothing(template, fwhm=np.deg2rad(fwhm))
# Define the residual
def residual(alpha, m, templates, mode):
dm = np.copy(m)
dm -= alpha * templates
map_variance = np.ones(len(m))
for i, d in enumerate(dm):
if mask is not None:
map_variance[i] = np.nansum((d[~mask] - np.nanmean(d[~mask]))**2)
else:
map_variance[i] = np.nansum((d - np.nanmean(d))**2)
if mode == 't':
return map_variance[0]
elif mode == 'p':
return map_variance[1] + map_variance[2]
else:
return map_variance.sum()
# Solve for the fit parameter
from scipy.optimize import minimize
if joint_fit:
f = minimize(residual, p0, args=(m, template, 1), method='Nelder-Mead')
fit_amp = np.nan
if f.success:
fit_amp = f.x
else:
f_I = minimize(residual, p0, args=(m, template, 't'), method='Nelder-Mead')
f_P = minimize(residual, p0, args=(m, template,'p' ), method='Nelder-Mead')
fit_amp = np.zeros(2) * np.nan
if f_I.success:
fit_amp[0] = f_I.x
if f_P.success:
fit_amp[1] = f_P.x
return fit_amp
def pts_mask_hfi(mmap, freq, input_coord, nside=2048, n=None):
"""
For HFI maps only. This function masks the n brighest (psfflux) points in mmap
with radius given by the planck beam.
Arguments
=========
mmap : numpy array
The input map to get point source masked.
freq : int
The pfreq channel to pick point sources from.
input_coord : string
Input coord. If not 'C', then the maps will be rotated to 'C', point sources are
masked, then rotate back to 'G' in the output
nside : int, optional
The nside of the input map.
n : int, optional
The number of brightest point source to be masked.
Returns
=======
The map with point source masked.
"""
if input_coord == 'G':
try:
mmap = rotate_map(mmap, coord=('G', 'C'))
except:
mmap = rotate_map(GtoIQU(mmap), coord=('G', 'C'))
path_to_points=os.path.join('/sptgrid/user/rgualtie/PlanckDR3Maps','planck_point_sources/')
pointspath=os.listdir(path_to_points)
points={}
for j in pointspath:
if '.tbl' in j:
key=int(j.strip('IpacTableFromSource_.t'))
points[key]=Table.read(os.path.join(path_to_points,j), format='ipac')
if not isinstance(freq, int):
freq = int(freq)
if not isinstance(n, int):
n = int(n)
for i in points:
points[i]=points[i][(points[i]['ra']>10) & (points[i]['ra']<100) & \
(points[i]['dec']<-16) & (points[i]['dec']>-58)]
points[i].sort('psfflux')
points[i].reverse()
r = 0.03*3 #100 arcmin radius
try:
cutmap=np.array(mmap)
except:
cutmap=np.array(G3toIQU(mmap))
for j,i in enumerate(points[freq][:n]):
ra=i['ra']
dec=i['dec']
theta=-dec*np.pi/180+np.pi/2
phi=ra*np.pi/180
vec=hp.ang2vec(theta,phi)
r=0.2*np.pi/180
disc=hp.query_disc(nside,vec,r)
for k in cutmap:
k[disc]=hp.UNSEEN
while (k[disc]==hp.UNSEEN).any():
for ii in disc:
if k[ii]==hp.UNSEEN:
neighbs=hp.pixelfunc.get_all_neighbours(nside,ii)
k[ii]=np.mean(k[neighbs][k[neighbs]!=hp.UNSEEN])
if input_coord == 'G':
cutmap = tools.rotate_map(cutmap, coord=('C', 'G'))
return cutmap
def ell2deg(ell):
return 180./60.*np.sqrt(2./np.pi/ell)
def plot_spectra(mm1=None, mm2=None, lowell=True, title='', label=''):
'''Input a map or two maps and plot the spectra'''
ellb, dlsb, errb = cs.spectrum_spice(map1=mm1, map2=mm2, lmin=8, lmax=1500, bin_width=25, mask=mask, lfac=True, return_error=True)
fig, ax = plt.subplots(2,3, sharex=True)
titles = ['TT','EE','BB','TE','EB','TB']
plt.suptitle(title)
for i in range(6):
ax[i//3,i%3].set_title(titles[i])
ax[i//3,i%3].plot(th_specs[i], 'k-', label='$\Lambda$-CDM')
ax[i//3,i%3].errorbar(ellb, dlsb[i], yerr=errb[i], fmt='o', label=label)
if i>2:
ax[i//3,i%3].set_xlabel('Multipole $\ell$')
if i==0 or i==3:
ax[i//3,i%3].set_ylabel('$D_\ell$ $[\mu K^2]$')
if lowell:
ax[i//3,i%3].set_xlim(0,500)
else:
ax[i//3,i%3].set_xlim(0,1500)
ax[i//3,i%3].legend(); ax[i//3,i%3].grid()
return True
"""
Tools for FITS world coordinte system 2D projections. Mainly for plotting.
Requires astropy.
For more info on WCS, see: https://arxiv.org/pdf/astro-ph/0207413.pdf
"""
__all__ = ["get_wcs_for_region", "healpix_to_wcs", "wcs_subplot",
"wcs_plot_world", "wcsview"]
def get_wcs_for_region(region="cmb", coord="C", projection="ZEA", **kwargs):
"""
Get the pre-defined WCS object for a named region
Arguments
=========
region : str
The name of the region. Default: "cmb"
coord : str
Coordinate system: "C" for equatorial (the default),
or "G" for galactic, "E" for ecliptic
projection : str
The WCS code for the projection type.
Defaults to "ZEA": zenithal (azimuthal) equal area.
Keyword arguments are used to update WCS parameters from the defaults.
For example: crval=[42., -42.]
Returns
=======
an astropy.wcs.WCS object for the region
"""
from astropy import wcs