1; ----------------------------------------------------------------------------- 2; 3; Copyright (C) 1997-2013 Krzysztof M. Gorski, Eric Hivon, Anthony J. Banday 4; 5; 6; 7; 8; 9; This file is part of HEALPix. 10; 11; HEALPix is free software; you can redistribute it and/or modify 12; it under the terms of the GNU General Public License as published by 13; the Free Software Foundation; either version 2 of the License, or 14; (at your option) any later version. 15; 16; HEALPix is distributed in the hope that it will be useful, 17; but WITHOUT ANY WARRANTY; without even the implied warranty of 18; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19; GNU General Public License for more details. 20; 21; You should have received a copy of the GNU General Public License 22; along with HEALPix; if not, write to the Free Software 23; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24; 25; For more information about HEALPix see http://healpix.sourceforge.net 26; 27; ----------------------------------------------------------------------------- 28pro remove_dipole, map, weight, $ 29 bad_data = bad_data, $ 30 gal_cut = gal_cut, $ 31 coord_in = u_coord_in, $ 32 coord_out= u_coord_out, $ 33 covariance_matrix = covariance, $ 34 dipole = dipole, $ 35 monopole = monopole, $ 36 noremove = noremove, $ 37 nside = nside_usr, $ 38 onlymonopole=onlymonopole, $ 39 ordering= ordering, $ 40 pixel = pixel, $ 41 units = units, $ 42 help = help, $ 43 silent = silent 44;+ 45; NAME: 46; remove_dipole 47; 48; PURPOSE: 49; remove best fit monopole and dipole (simultaneous fit) 50; 51; CATEGORY: 52; Healpix map processing 53; 54; CALLING SEQUENCE: 55; remove_dipole, map, [weight, BAD_DATA=, GAL_CUT=, COORD_IN=, COORD_OUT=, $ 56; COVARIANCE_MATRIX=, DIPOLE=, MONOPOLE=, $ 57; NOREMOVE=, NSIDE=, ONLYMONOPOLE=, ORDERING=, PIXEL=, UNITS=, $ 58; HELP=, SILENT=] 59; 60; INPUTS: 61; map : array on which monopole and dipole are to be removed 62; (also used for output) 63; assumed to be a full sky data set, unless pixel is set and has the same 64; size as map 65; 66; OPTIONAL INPUTS: 67; weight : array of same size as map, 68; describe weighting to apply to each pixel for the fit 69; 70; KEYWORD PARAMETERS: 71; BAD_DATA : scalar float, value given on input to bad pixels 72; default = !healpix.bad_value = -1.63750000e+30 73; GAL_CUT : the pixels with galactic latitude |b|<gal_cut are not considered in the 74; fit. In Degrees, in [0, 90] 75; COORD_IN : coordinate system (either 'Q' or 'C' or 'q' or 'c' equatorial, 76; 'G'/'g' galactic or 'E'/'e' ecliptic) of the input map 77; default = 'G' (galactic) 78; COORD_OUT : coordinate system in which to output dipole vector in Dipole 79; default = same as coord_in 80; NOREMOVE : if set, the dipole and monopole are computed but not removed 81; NSIDE : scalar integer, healpix resolution parameter 82; ONLYMONOPOLE : fit and remove only the monopole 83; ORDERING : string, describe pixelisation (either 'RING' or 'NESTED') 84; PIXEL : vector, gives the actual list of pixels whose temperature is given in map 85; useful in case of very limited sky coverage 86; UNITS : units of the input map 87; HELP: displays this information header 88; SILENT: works silently (except for error messages) 89; 90; OUTPUTS: 91; map : contains the map minus monopole and dipole 92; unless NOREMOVE is set 93; 94; OPTIONAL OUTPUTS: 95; COVARIANCE_MATRIX: scalar (or symmetric 4x4 matrix) containing the covariance 96; of the statistical errors made on monopole (and dipole) determination 97; DIPOLE : 3-vector : amplitude and direction of dipole 98; MONOPOLE : amplitude of monopole, same units as map 99; 100; SIDE EFFECTS: 101; 102; 103; 104; PROCEDURE: 105; least square fit 106; directly hacked from A. J. Banday's Dipole 107; 'optimized' to reduce memory requirement 108; 109; EXAMPLE: 110; 111; 112; 113; MODIFICATION HISTORY: 114; 2000-02-16, EH, Caltech, 1.0 115; 2002-08-16, EH, Caltech, 1.1 cosmetic editions 116; 2006-06-23, EH, IAP: total() -> total(,/double) for improved accuracy 117; 2008-08-21, EH, IAP: accept Nside>8192; added /HELP keyword; slight speed 118; increase 119; 2009-10-30, EH, IAP: replaced obsolete SVD with SVDC (+ SVSOL) 120; monopole and dipole now output in double precision 121; 2009-12-11, EH, IAP: COORD_IN and COORD_OUT can be upper or lower case 122; add SILENT keyword 123; 2010-03-31, EH, IAP: compute covariance matrix; 124; issue error message if map is not 1-dimensional. 125;- 126 127defsysv, '!healpix', exists = exists 128if (exists ne 1) then init_healpix 129 130syntax = 'remove_dipole, map, [weight, bad_data=, gal_cut=, covariance_matrix=, coord_in=, coord_out=, dipole=, monopole=, noremove=, nside=, onlymonopole=, ordering=, pixel=, units=, help=, silent=]' 131 132code = 'remove_dipole' 133if keyword_set(help) then begin 134 doc_library,code 135 return 136endif 137 138if n_params() lt 1 or n_params() gt 2 then begin 139 print,syntax 140 if n_params() gt 2 then message,'Abort' else return 141endif 142be_verbose = ~keyword_set(silent) 143loadsky 144 145obs_npix = n_elements(map) 146obs_npix2 = n_elements(weight) 147obs_npix3 = n_elements(pixel) 148if undefined(bad_data) then bad_data = !healpix.bad_value ;-1.63750000e+30 149sz = size(map) 150 151if (sz[0] gt 1) then begin 152 message,syntax,/info 153 message,/info,'Current input map has '+strtrim(sz[0],2)+' dimensions' 154 message,'Only 1 D maps are accepted.' 155endif 156 157dow8 = 0 158if (obs_npix2 gt 1) then begin 159 if (obs_npix2 ne obs_npix) then begin 160 message,syntax,/info 161 message,'inconsistent map and weight' 162 endif else begin 163 dow8 = 1 164 endelse 165endif 166 167partial = 0 168if (obs_npix3 gt 1) then begin 169 if (obs_npix3 ne obs_npix) then begin 170 message,syntax,/info 171 message,'inconsistent map and pixel' 172 endif else begin 173 partial = 1 174 endelse 175endif 176 177galcut = 0 178scut = ' ' 179if defined(gal_cut) then begin 180 if (gal_cut) gt 0. and gal_cut lt 90 then begin 181 galcut = 1 182 zcut = sin(gal_cut*!dtor) 183 sgal_cut = '|b| > '+string(gal_cut,form='(f7.3)')+' Deg' 184 endif 185endif 186coord_in = defined(u_coord_in) ? strmid(strtrim(strupcase(u_coord_in),2),0,1) : 'G' 187if coord_in EQ 'C' then coord_in = 'Q' 188fullcoord = decode_coord(coord_in,error=error_coord1) 189 190coord_out = defined(u_coord_out) ? strmid(strtrim(strupcase(u_coord_out),2),0,1) : coord_in 191if coord_out EQ 'C' then coord_out = 'Q' 192fullcoord_out = decode_coord(coord_out,error=error_coord2) 193 194if (error_coord1+error_coord2) ne 0 then begin 195 message,syntax,/info 196 message,'invalid choice of coordinate' 197endif 198 199if undefined(units) then units = ' ' 200 201dodipole = 1 202Vector = dblarr(4) 203Matrix = dblarr(4,4) 204if keyword_set(onlymonopole) then begin 205 dipole_out = 0 206 dodipole = 0 207 Vector = [0.d0] 208 Matrix = [0.d0] 209endif 210if (dodipole or galcut) then begin 211 if undefined(nside_usr) or undefined(ordering) then begin 212 print,code+': Nside and Ordering have to be known when '+$ 213 'removing dipole ' 214 return 215 endif 216endif 217if defined(ordering) then pix_type = STRUPCASE(STRMID(ordering,0,1)) 218healpix_data = (pix_type eq 'R' || pix_type eq 'N') 219if defined(nside_usr) then begin 220 pix_param = nside_usr 221 if (healpix_data && obs_npix gt nside2npix(nside_usr)) then begin 222 message,syntax,/info 223 message,'Inconsistent inputs:',/info 224 message,' Map size: '+strtrim(obs_npix,2),/info 225 message,' Nside= '+strtrim(nside_usr,2)+' -> Npix= '+strtrim(nside2npix(nside_usr),2),/info 226 message,'Map is too large.' 227 endif 228endif 229 230npiece = 24 231if (~dodipole && ~galcut) then npiece = 12 232stride = obs_npix/npiece > 1 233 234 235;-------------- mean square equation ------------------------------ 236;-------------- construct matrix and vector ------------------------ 237; do a loop to minimize arrays size 238for is = 0,npiece do begin 239 imin = is*stride 240 imax = (imin+stride-1) < (obs_npix-1) 241 if (imin gt imax) then goto, enough 242 map_tmp = map[imin:imax] 243 ; replace NaN by bad_data to allow calculations 244 nan = where(finite(map_tmp,/nan), nnan) 245 if (nnan gt 0) then map_tmp[nan] = bad_data 246 nan = 0 247 248 ; pixel weight 249 flag = (abs(map_tmp/bad_data-1.) gt .01) ; byte array, either 1 for good pixels or 0 for bad ones 250 if dow8 then flag = flag*weight[imin:imax] 251 252 if (dodipole or galcut) then begin 253 ; compute pixel position from pixel number 254 if partial then begin 255 id_pix = pixel[imin:imax] 256 endif else begin 257 id_pix = (pix_param gt 8192) ? lindgen64(imax-imin+1)+imin : lindgen(imax-imin+1)+imin 258 endelse 259 case pix_type of 260 'R' : PIX2VEC_RING, pix_param, id_pix, vec ; Healpix ring 261 'N' : PIX2VEC_NEST, pix_param, id_pix, vec ; Healpix nest 262 'Q' : vec = PIX2UV(id_pix, pix_param) ; QuadCube (COBE cgis software) 263 else : print,code+': error on pix_type' 264 endcase 265 if (galcut) then begin 266 if coord_in ne 'G' then vecg = SKYCONV(vec, inco = coord_in, outco = 'G') else vecg=vec 267 flagg = abs(vecg[*,2]) gt zcut ; 1 for pixels outside galactic cut, 0 otherwise 268 flag = flag*flagg 269 vecg = 0 270 endif 271 endif 272 273 if dodipole then begin 274 temp = map_tmp*flag 275 Vector[0] += total(temp, /double) 276 Vector[1] += total(temp*vec[*,0], /double) 277 Vector[2] += total(temp*vec[*,1], /double) 278 Vector[3] += total(temp*vec[*,2], /double) 279 endif else begin 280 Vector[0] += total(map_tmp*flag, /double) 281 endelse 282 Matrix[0,0] += TOTAL(flag, /double) 283 if dodipole then begin 284 for i = 1,3 do begin 285 Matrix[i,0] += TOTAL(vec[*,i-1]*flag, /double) 286 for j = 1,i do begin ; only fill one half of symmetric matrix 287 Matrix[i,j] += TOTAL(vec[*,i-1]*vec[*,j-1]*flag, /double) 288 endfor 289 endfor 290 endif 291endfor 292enough: 293 294do_cov = arg_present(covariance) 295 296if (be_verbose) then print,'Best fit: '+scut 297if ~dodipole then begin 298 monopole = Vector[0]/Matrix[0] 299 if (be_verbose) then print,'Monopole ['+units+']: ',monopole 300 covariance = 1.d0/Matrix[0] 301endif else begin 302 ; prepare identity matrix for computation of covariance matrix 303 if (do_cov) then begin 304 covariance = dblarr(4,4) 305 if (is_gdl()) then begin 306 identity = dblarr(4,4) 307 for i=0,3 do identity[i,i] = 1.d0 308 endif else begin 309 identity = diag_matrix(replicate(1.d0,4)) 310 endelse 311 endif 312 313 ; fill other half of matrix by symmetry 314 for i=1,3 do Matrix[0:i-1,i] = Matrix[i,0:i-1] 315 316 ; check matrix diagonal 317 err_on_mat = abs((Matrix[1,1]+Matrix[2,2]+Matrix[3,3])/Matrix[0,0]-1.d0) 318 if (err_on_mat gt 1.d-10) then begin 319 print, err_on_mat 320 message,'Error in matrix construction' 321 endif 322 323 ; use SVD to invert matrix M and solve, A is inverse of Single Value 324 ; Decomposition of M 325 ; 2015-06-30: corrected substitute for SVSOL in GDL case (only makes a difference for non-symmetric matrix) 326 ndim = 4 327 SVDC, Matrix, w, U, V, /double ; w contains (positive) eigenvalues 328 w_threshold = max(abs(w)) * 1.0d-06 329 if (is_gdl()) then begin ; GDL: no SVSOL, no DIAG_MATRIX 330 nw = n_elements(w) 331 Wp = dblarr(nw,nw) 332 large = where(abs(w) GT w_threshold, count) ; find large eigenvalues 333 if (count gt 0) then Wp[large, large] = 1.d0/w[large] ; only invert those 334 ;;;;;;;;C = transpose(U) # ( Wp # (V # Vector)) 335 C = transpose(V) # ( Wp # (U # Vector)) ; should be faster than expected (U ## Wp ## Transpose(V)) # Vector 336 if (do_cov) then begin 337 ;;;;;;for i=0,3 do covariance[*,i] = transpose(U) # ( Wp # (V # identity[*,i])) 338 for i=0,ndim-1 do covariance[*,i] = transpose(V) # ( Wp # (U # identity[*,i])) 339 endif 340 endif else begin 341 w = w * ( abs(w) gt w_threshold) ; set low eigenvalues to zero 342 C = SVSOL( U, w, V, Vector, /double) 343 if (do_cov) then begin 344 for i=0,ndim-1 do covariance[*,i] = SVSOL( U, w, V, identity[*,i], /double) 345 endif 346 endelse 347 ; force symmetry of covariance matrix 348 if (do_cov) then begin 349 covariance = (covariance + transpose(covariance))*0.5d0 350 endif 351 352 ;;;C = float(C) 353 Monopole = C[0] 354 Dipole = C[1:3] 355 356 if (be_verbose) then print,'Monopole ['+units+']: ',monopole 357 dip_ampli = sqrt(total(dipole^2, /double)) 358 if (be_verbose) then print,'Dipole : amplitude: ',dip_ampli 359 vec2ang,dipole,lat,long,/astro 360 if (be_verbose) then print,fullcoord+' coordinates [Deg]: ',long, lat,form='(a,f7.2,f7.2)' 361 dipole_out = dipole 362 if (coord_in ne coord_out) then begin 363 dipole_out = SKYCONV(dipole, inco = coord_in, outco = coord_out) 364 vec2ang,dipole_out,lat,long,/astro 365 if (be_verbose) then print,fullcoord_out+' coordinates [Deg]: ',long, lat,form='(a,f7.2,f7.2)' 366 endif 367endelse 368 369; correct map for monopole and dipole 370 371if ~keyword_set(noremove) then begin 372 373; do a loop to minimize arrays size 374 for is=0,npiece do begin 375 imin = is*stride 376 imax = (imin+stride-1) < (obs_npix-1) 377 if (imin gt imax) then goto, done 378 map_tmp = map[imin:imax] 379 380 ; replace NaN by bad_data to allow calculations 381 nan = where(finite(map_tmp,/nan), nnan) 382 if (nnan gt 0) then map_tmp[nan] = bad_data 383 nan = 0 384 385 good = imin + where( abs(map_tmp/bad_data-1.) gt .01, ngood ) 386 387 if (ngood gt 0) then begin 388 if (dodipole) then begin 389 ; compute pixel position from pixel number 390 if partial then id_pix = pixel[good] $ 391 else id_pix = good 392 case pix_type of 393 'R' : PIX2VEC_RING, pix_param, id_pix, vec ; Healpix ring 394 'N' : PIX2VEC_NEST, pix_param, id_pix, vec ; Healpix nest 395 'Q' : vec = PIX2UV(id_pix, pix_param) ; QuadCube (COBE cgis software) 396 else : print,code+': error on pix_type' 397 endcase 398 map[good] = map[good] - (monopole + dipole ## vec ) 399 endif else begin 400 map[good] = map[good] - monopole 401 endelse 402 endif 403 404 endfor 405done: 406endif 407 408dipole = dipole_out 409 410return 411end 412 413