Coverage for python/lsst/sims/featureScheduler/utils/footprints.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Footprints: Some relevant LSST footprints, including utilities to build them.
3The goal here is to make it easy to build typical target maps and then their associated combined
4survey inputs (maps in each filter, including scaling between filters; the associated cloud and
5sky brightness maps that would have limits for WFD, etc.).
7For generic use for defining footprints from scratch, there is also a utility that simply generates
8the healpix points across the sky, along with their corresponding RA/Dec/Galactic l,b/Ecliptic l,b values.
9"""
11import os
12import numpy as np
13import healpy as hp
14from astropy.coordinates import SkyCoord
15from astropy import units as u
16from .utils import set_default_nside, int_rounded
17from lsst.sims.utils import _hpid2RaDec, _angularSeparation
18from lsst.sims.utils import Site
19from lsst.utils import getPackageDir
21__all__ = ['ra_dec_hp_map', 'generate_all_sky', 'get_dustmap',
22 'WFD_healpixels', 'WFD_no_gp_healpixels', 'WFD_bigsky_healpixels', 'WFD_no_dust_healpixels',
23 'SCP_healpixels', 'NES_healpixels',
24 'galactic_plane_healpixels', #'low_lat_plane_healpixels', 'bulge_healpixels',
25 'magellanic_clouds_healpixels', 'Constant_footprint',
26 'generate_goal_map', 'standard_goals',
27 'calc_norm_factor', 'filter_count_ratios', 'Step_line', 'Footprints', 'Footprint',
28 'Step_slopes', 'Base_pixel_evolution']
31class Base_pixel_evolution(object):
32 """Helper class that can be used to describe the time evolution of a HEALpix in a footprint
33 """
35 def __init__(self, period=365.25, rise=1., t_start=0.):
36 self.period = period
37 self.rise = rise
38 self.t_start = t_start
40 def __call__(self, mjd_in, phase):
41 pass
44class Step_line(Base_pixel_evolution):
45 """
46 Parameters
47 ----------
48 period : float (365.25)
49 The period to use
50 rise : float (1.)
51 How much the curve should rise every period
52 """
53 def __call__(self, mjd_in, phase):
54 t = mjd_in+phase - self.t_start
55 n_periods = np.floor(t/(self.period))
56 result = n_periods*self.rise
57 tphased = t % self.period
58 step_area = np.where(tphased > self.period/2.)[0]
59 result[step_area] += (tphased[step_area] - self.period/2)*self.rise/(0.5*self.period)
60 result[np.where(t < 0)] = 0
61 return result
64class Step_slopes(Base_pixel_evolution):
65 """
66 Parameters
67 ----------
68 period : float (365.25)
69 The period to use
70 rise : np.array-like
71 How much the curve should rise each period.
72 """
73 def __call__(self, mjd_in, phase):
74 steps = np.array(self.rise)
75 t = mjd_in+phase - self.t_start
76 season = np.floor(t/(self.period))
77 season = season.astype(int)
78 plateus = np.cumsum(steps)-steps[0]
79 result = plateus[season]
80 tphased = t % self.period
81 step_area = np.where(tphased > self.period/2.)[0]
82 result[step_area] += (tphased[step_area] - self.period/2)*steps[season+1][step_area]/(0.5*self.period)
83 result[np.where(t < 0)] = 0
85 return result
88class Footprint(object):
89 """An object to compute the desired survey footprint at a given time
91 Parameters
92 ----------
93 mjd_start : float
94 The MJD the survey starts on
95 sun_RA_start : float
96 The RA of the sun at the start of the survey (radians)
98 """
99 def __init__(self, mjd_start, sun_RA_start=0, nside=32,
100 filters={'u': 0, 'g': 1, 'r': 2, 'i': 3, 'z': 4, 'y': 5},
101 period=365.25, step_func=None):
102 self.period = period
103 self.nside = nside
104 if step_func is None:
105 step_func = Step_line()
106 self.step_func = step_func
107 self.mjd_start = mjd_start
108 self.sun_RA_start = sun_RA_start
109 self.npix = hp.nside2npix(nside)
110 self.filters = filters
111 self.ra, self.dec = _hpid2RaDec(self.nside, np.arange(self.npix))
112 # Set the phase of each healpixel. If RA to sun is zero, we are at phase np.pi/2.
113 self.phase = (-self.ra + self.sun_RA_start + np.pi/2) % (2.*np.pi)
114 self.phase = self.phase * (self.period/2./np.pi)
115 # Empty footprints to start
116 self.out_dtype = list(zip(filters, [float]*len(filters)))
117 self.footprints = np.zeros((len(filters), self.npix), dtype=float)
118 self.estimate = np.zeros((len(filters), self.npix), dtype=float)
119 self.current_footprints = np.zeros((len(filters), self.npix), dtype=float)
120 self.zero = self.step_func(0., self.phase)
121 self.mjd_current = None
123 def set_footprint(self, filtername, values):
124 self.footprints[self.filters[filtername], :] = values
126 def get_footprint(self, filtername):
127 return self.footprints[self.filters[filtername], :]
129 def _update_mjd(self, mjd, norm=True):
130 if mjd != self.mjd_current:
131 self.mjd_current = mjd
132 t_elapsed = mjd - self.mjd_start
134 norm_coverage = self.step_func(t_elapsed, self.phase)
135 norm_coverage -= self.zero
136 self.current_footprints = self.footprints * norm_coverage
137 c_sum = np.sum(self.current_footprints)
138 if norm:
139 if c_sum != 0:
140 self.current_footprints = self.current_footprints/c_sum
142 def arr2struc(self, inarr):
143 """take an array and convert it to labled struc array
144 """
145 result = np.empty(self.npix, dtype=self.out_dtype)
146 for key in self.filters:
147 result[key] = inarr[self.filters[key]]
148 # Argle bargel, why doesn't this view work?
149 # struc = inarr.view(dtype=self.out_dtype).squeeze()
150 return result
152 def estimate_counts(self, mjd, nvisits=2.2e6, fov_area=9.6):
153 """Estimate the counts we'll get after some time and visits
154 """
155 pix_area = hp.nside2pixarea(self.nside, degrees=True)
156 pix_per_visit = fov_area/pix_area
157 self._update_mjd(mjd, norm=True)
158 self.estimate = self.current_footprints * pix_per_visit * nvisits
159 return self.arr2struc(self.estimate)
161 def __call__(self, mjd, array=False, norm=True):
162 """
163 Returns
164 -------
165 a numpy array with the normalized number of observations that should be at each HEALpix.
166 Multiply by the number of HEALpix observations (all filters), to convert to the number of observations
167 desired.
168 """
169 self._update_mjd(mjd, norm=norm)
170 #if array:
171 # return self.current_footprints
172 #else:
173 return self.arr2struc(self.current_footprints)
176class Constant_footprint(Footprint):
177 def __init__(self, nside=32,
178 filters={'u': 0, 'g': 1, 'r': 2, 'i': 3, 'z': 4, 'y': 5}):
179 self.nside = nside
180 self.filters = filters
181 self.npix = hp.nside2npix(nside)
182 self.footprints = np.zeros((len(filters), self.npix), dtype=float)
183 self.out_dtype = list(zip(filters, [float]*len(filters)))
184 self.to_return = self.arr2struc(self.footprints)
186 def __call__(self, mjd, array=False):
187 return self.to_return
191class Footprints(Footprint):
192 """An object to combine multiple Footprint objects.
193 """
194 def __init__(self, footprint_list):
195 self.footprint_list = footprint_list
196 self.mjd_current = None
197 self.current_footprints = 0
198 # Should probably run a check that all the footprints are compatible (same nside, etc)
199 self.npix = footprint_list[0].npix
200 self.out_dtype = footprint_list[0].out_dtype
201 self.filters = footprint_list[0].filters
202 self.nside = footprint_list[0].nside
204 self.footprints = np.zeros((len(self.filters), self.npix), dtype=float)
205 for Fp in self.footprint_list:
206 self.footprints += Fp.footprints
208 def set_footprint(self, filtername, values):
209 pass
211 def _update_mjd(self, mjd, norm=True):
212 if mjd != self.mjd_current:
213 self.mjd_current = mjd
214 self.current_footprints = 0.
215 for fp in self.footprint_list:
216 fp._update_mjd(mjd, norm=False)
217 self.current_footprints += fp.current_footprints
218 c_sum = np.sum(self.current_footprints)
219 if norm:
220 if c_sum != 0:
221 self.current_footprints = self.current_footprints/c_sum
224def ra_dec_hp_map(nside=None):
225 """
226 Return all the RA,dec points for the centers of a healpix map, in radians.
227 """
228 if nside is None:
229 nside = set_default_nside()
230 ra, dec = _hpid2RaDec(nside, np.arange(hp.nside2npix(nside)))
231 return ra, dec
234def get_dustmap(nside=None):
235 if nside is None:
236 nside = set_default_nside()
237 ebvDataDir = getPackageDir('sims_maps')
238 filename = 'DustMaps/dust_nside_%i.npz' % nside
239 dustmap = np.load(os.path.join(ebvDataDir, filename))['ebvMap']
240 return dustmap
243def generate_all_sky(nside=None, elevation_limit=20, mask=hp.UNSEEN):
244 """Set up a healpix map over the entire sky.
245 Calculate RA & Dec, Galactic l & b, Ecliptic l & b, for all healpixels.
246 Calculate max altitude, to set to areas which LSST cannot reach (set these to hp.unseen).
248 This is intended to be a useful tool to use to set up target maps, beyond the standard maps
249 provided in these utilities. Masking based on RA, Dec, Galactic or Ecliptic lat and lon is easier.
251 Parameters
252 ----------
253 nside : int, opt
254 Resolution for the healpix maps.
255 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
256 elevation_limit : float, opt
257 Elevation limit for map.
258 Parts of the sky which do not reach this elevation limit will be set to mask.
259 mask : float, opt
260 Mask value for 'unreachable' parts of the sky, defined as elevation < 20.
261 Note that the actual limits will be set elsewhere, using the observatory model.
262 This limit is for use when understanding what the maps could look like.
264 Returns
265 -------
266 dict of np.ndarray
267 Returns map, RA/Dec, Gal l/b, Ecl l/b (each an np.ndarray IN RADIANS) in a dictionary.
268 """
269 if nside is None:
270 nside = set_default_nside()
272 # Calculate coordinates of everything.
273 skymap = np.zeros(hp.nside2npix(nside), float)
274 ra, dec = ra_dec_hp_map(nside=nside)
275 coord = SkyCoord(ra=ra * u.rad, dec=dec * u.rad, frame='icrs')
276 eclip_lat = coord.barycentrictrueecliptic.lat.deg
277 eclip_lon = coord.barycentrictrueecliptic.lon.deg
278 gal_lon = coord.galactic.l.deg
279 gal_lat = coord.galactic.b.deg
281 # Calculate max altitude (when on meridian).
282 lsst_site = Site('LSST')
283 elev_max = np.pi / 2. - np.abs(dec - lsst_site.latitude_rad)
284 skymap = np.where(int_rounded(elev_max) >= int_rounded(np.radians(elevation_limit), skymap, mask))
286 return {'map': skymap, 'ra': np.degrees(ra), 'dec': np.degrees(dec),
287 'eclip_lat': eclip_lat, 'eclip_lon': eclip_lon,
288 'gal_lat': gal_lat, 'gal_lon': gal_lon}
291def WFD_healpixels(nside=None, dec_min=-62.5, dec_max=3.6):
292 """
293 Define a region based on declination limits only.
295 Parameters
296 ----------
297 nside : int, opt
298 Resolution for the healpix maps.
299 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
300 dec_min : float, opt
301 Minimum declination of the region (deg). Default -62.5.
302 dec_max : float, opt
303 Maximum declination of the region (deg). Default 3.6.
305 Returns
306 -------
307 np.ndarray
308 Healpix map with regions in declination-limited 'wfd' region as 1.
309 """
310 if nside is None:
311 nside = set_default_nside()
313 ra, dec = ra_dec_hp_map(nside=nside)
314 result = np.zeros(ra.size, float)
315 dec = int_rounded(dec)
316 good = np.where((dec >= int_rounded(np.radians(dec_min))) &
317 (dec <= int_rounded(np.radians(dec_max))))
318 result[good] = 1
319 return result
322def WFD_no_gp_healpixels(nside, dec_min=-62.5, dec_max=3.6,
323 center_width=10., end_width=4., gal_long1=290., gal_long2=70.):
324 """
325 Define a wide fast deep region with a galactic plane limit.
327 Parameters
328 ----------
329 nside : int, opt
330 Resolution for the healpix maps.
331 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
332 dec_min : float, opt
333 Minimum declination of the region (deg).
334 dec_max : float, opt
335 Maximum declination of the region (deg).
336 center_width : float, opt
337 Width across the central part of the galactic plane region.
338 end_width : float, opt
339 Width across the remainder of the galactic plane region.
340 gal_long1 : float, opt
341 Longitude at which to start tapering from center_width to end_width.
342 gal_long2 : float, opt
343 Longitude at which to stop tapering from center_width to end_width.
345 Returns
346 -------
347 np.ndarray
348 Healpix map with regions in declination-limited 'wfd' region as 1.
349 """
350 wfd_dec = WFD_healpixels(nside, dec_min=dec_min, dec_max=dec_max)
351 gp = galactic_plane_healpixels(nside=nside, center_width=center_width, end_width=end_width,
352 gal_long1=gal_long1, gal_long2=gal_long2)
353 sky = np.where(wfd_dec - gp > 0, wfd_dec - gp, 0)
354 return sky
357def WFD_bigsky_healpixels(nside):
358 sky = WFD_no_gp_healpixels(nside, dec_min=-72.25, dec_max=12.4, center_width=14.9,
359 gal_long1=0, gal_long2=360)
360 return sky
363def WFD_no_dust_healpixels(nside, dec_min=-72.25, dec_max=12.4, dust_limit=0.19):
364 """Define a WFD region with a dust extinction limit.
366 Parameters
367 ----------
368 nside : int, opt
369 Resolution for the healpix maps.
370 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
371 dec_min : float, opt
372 Minimum dec of the region (deg). Default -72.5 deg.
373 dec_max : float, opt.
374 Maximum dec of the region (deg). Default 12.5 deg.
375 1.75 is the FOV radius in deg.
376 dust_limit : float, None
377 Remove pixels with E(B-V) values greater than dust_limit from the footprint.
379 Returns
380 -------
381 result : numpy array
382 """
383 if nside is None:
384 nside = set_default_nside()
386 ra, dec = ra_dec_hp_map(nside=nside)
387 dustmap = get_dustmap(nside)
389 result = np.zeros(ra.size, float)
390 # First set based on dec range.
391 dec = int_rounded(dec)
392 good = np.where((dec >= int_rounded(np.radians(dec_min))) &
393 (dec <= int_rounded(np.radians(dec_max))))
394 result[good] = 1
395 # Now remove areas with dust extinction beyond the limit.
396 result = np.where(dustmap >= dust_limit, 0, result)
397 return result
400def SCP_healpixels(nside=None, dec_max=-60.):
401 """
402 Define the South Celestial Pole region. Return a healpix map with SCP pixels as 1.
403 """
404 if nside is None:
405 nside = set_default_nside()
407 ra, dec = ra_dec_hp_map(nside=nside)
408 result = np.zeros(ra.size, float)
409 good = np.where(int_rounded(dec) < int_rounded(np.radians(dec_max)))
410 result[good] = 1
411 return result
414def NES_healpixels(nside=None, min_EB=-30.0, max_EB = 10.0, dec_min=2.8):
415 """
416 Define the North Ecliptic Spur region. Return a healpix map with NES pixels as 1.
418 Parameters
419 ----------
420 nside : int
421 A valid healpix nside
422 min_EB : float (-30.)
423 Minimum barycentric true ecliptic latitude (deg)
424 max_EB : float (10.)
425 Maximum barycentric true ecliptic latitude (deg)
426 dec_min : float (2.8)
427 Minimum dec in degrees
429 Returns
430 -------
431 result : numpy array
432 """
433 if nside is None:
434 nside = set_default_nside()
436 ra, dec = ra_dec_hp_map(nside=nside)
437 result = np.zeros(ra.size, float)
438 coord = SkyCoord(ra=ra*u.rad, dec=dec*u.rad)
439 eclip_lat = coord.barycentrictrueecliptic.lat.radian
440 eclip_lat = int_rounded(eclip_lat)
441 dec = int_rounded(dec)
442 good = np.where((eclip_lat > int_rounded(np.radians(min_EB))) &
443 (eclip_lat < int_rounded(np.radians(max_EB))) &
444 (dec > int_rounded(np.radians(dec_min))))
445 result[good] = 1
447 return result
450def galactic_plane_healpixels(nside=None, center_width=10., end_width=4.,
451 gal_long1=290., gal_long2=70.):
452 """
453 Define a Galactic Plane region.
455 Parameters
456 ----------
457 nside : int, opt
458 Resolution for the healpix maps.
459 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
460 center_width : float, opt
461 Width at the center of the galactic plane region.
462 end_width : float, opt
463 Width at the remainder of the galactic plane region.
464 gal_long1 : float, opt
465 Longitude at which to start the GP region.
466 gal_long2 : float, opt
467 Longitude at which to stop the GP region.
468 Order matters for gal_long1 / gal_long2!
470 Returns
471 -------
472 np.ndarray
473 Healpix map with galactic plane regions set to 1.
474 """
475 if nside is None:
476 nside = set_default_nside()
477 ra, dec = ra_dec_hp_map(nside=nside)
479 coord = SkyCoord(ra=ra*u.rad, dec=dec*u.rad)
480 gal_lon, gal_lat = coord.galactic.l.deg, coord.galactic.b.deg
481 # Reject anything beyond the central width.
482 sky = np.where(np.abs(gal_lat) < center_width, 1, 0)
483 # Apply the galactic longitude cuts, so that plane goes between gal_long1 to gal_long2.
484 # This is NOT the shortest distance between the angles.
485 gp_length = (gal_long2 - gal_long1) % 360
486 # If the length is greater than 0 then we can add additional cuts.
487 if gp_length > 0:
488 # First, remove anything outside the gal_long1/gal_long2 region.
489 sky = np.where(int_rounded((gal_lon - gal_long1) % 360) < int_rounded(gp_length), sky, 0)
490 # Add the tapers.
491 # These slope from the center (gp_center @ center_width)
492 # to the edge (gp_center + gp_length/2 @ end_width).
493 half_width = gp_length / 2.
494 slope = (center_width - end_width) / half_width
495 gp_center = (gal_long1 + half_width) % 360
496 gp_dist = gal_lon - gp_center
497 gp_dist = np.abs(np.where(int_rounded(gp_dist) > int_rounded(180), (180 - gp_dist) % 180, gp_dist))
498 lat_limit = np.abs(center_width - slope * gp_dist)
499 sky = np.where(int_rounded(np.abs(gal_lat)) < int_rounded(lat_limit), sky, 0)
500 return sky
503def magellanic_clouds_healpixels(nside=None, lmc_radius=10, smc_radius=5):
504 """
505 Define the Galactic Plane region. Return a healpix map with GP pixels as 1.
506 """
507 if nside is None:
508 nside = set_default_nside()
509 ra, dec = ra_dec_hp_map(nside=nside)
510 result = np.zeros(hp.nside2npix(nside))
512 lmc_ra = np.radians(80.893860)
513 lmc_dec = np.radians(-69.756126)
514 lmc_radius = np.radians(lmc_radius)
516 smc_ra = np.radians(13.186588)
517 smc_dec = np.radians(-72.828599)
518 smc_radius = np.radians(smc_radius)
520 dist_to_lmc = _angularSeparation(lmc_ra, lmc_dec, ra, dec)
521 lmc_pix = np.where(int_rounded(dist_to_lmc) < int_rounded(lmc_radius))
522 result[lmc_pix] = 1
524 dist_to_smc = _angularSeparation(smc_ra, smc_dec, ra, dec)
525 smc_pix = np.where(int_rounded(dist_to_smc) < int_rounded(smc_radius))
526 result[smc_pix] = 1
527 return result
530def generate_goal_map(nside=None, NES_fraction = .3, WFD_fraction = 1.,
531 SCP_fraction=0.4, GP_fraction = 0.2,
532 NES_min_EB = -30., NES_max_EB = 10, NES_dec_min = 3.6,
533 SCP_dec_max=-62.5, gp_center_width=10.,
534 gp_end_width=4., gp_long1=290., gp_long2=70.,
535 wfd_dec_min=-62.5, wfd_dec_max=3.6,
536 generate_id_map=False):
537 """
538 Handy function that will put together a target map in the proper order.
539 """
540 if nside is None:
541 nside = set_default_nside()
543 # Note, some regions overlap, thus order regions are added is important.
544 result = np.zeros(hp.nside2npix(nside), dtype=float)
545 id_map = np.zeros(hp.nside2npix(nside), dtype=int)
546 pid = 1
547 prop_name_dict = dict()
549 if NES_fraction > 0.:
550 nes = NES_healpixels(nside=nside, min_EB = NES_min_EB, max_EB = NES_max_EB,
551 dec_min=NES_dec_min)
552 result[np.where(nes != 0)] = 0
553 result += NES_fraction*nes
554 id_map[np.where(nes != 0)] = 1
555 pid += 1
556 prop_name_dict[1] = 'NorthEclipticSpur'
558 if WFD_fraction > 0.:
559 wfd = WFD_healpixels(nside=nside, dec_min=wfd_dec_min, dec_max=wfd_dec_max)
560 result[np.where(wfd != 0)] = 0
561 result += WFD_fraction*wfd
562 id_map[np.where(wfd != 0)] = 3
563 pid += 1
564 prop_name_dict[3] = 'WideFastDeep'
566 if SCP_fraction > 0.:
567 scp = SCP_healpixels(nside=nside, dec_max=SCP_dec_max)
568 result[np.where(scp != 0)] = 0
569 result += SCP_fraction*scp
570 id_map[np.where(scp != 0)] = 2
571 pid += 1
572 prop_name_dict[2] = 'SouthCelestialPole'
574 if GP_fraction > 0.:
575 gp = galactic_plane_healpixels(nside=nside, center_width=gp_center_width,
576 end_width=gp_end_width, gal_long1=gp_long1,
577 gal_long2=gp_long2)
578 result[np.where(gp != 0)] = 0
579 result += GP_fraction*gp
580 id_map[np.where(gp != 0)] = 4
581 pid += 1
582 prop_name_dict[4] = 'GalacticPlane'
584 if generate_id_map:
585 return result, id_map, prop_name_dict
586 else:
587 return result
590def standard_goals(nside=None):
591 """
592 A quick function to generate the "standard" goal maps. This is the traditional WFD/mini survey footprint.
593 """
594 if nside is None:
595 nside = set_default_nside()
597 result = {}
598 result['u'] = generate_goal_map(nside=nside, NES_fraction=0.,
599 WFD_fraction=0.31, SCP_fraction=0.15,
600 GP_fraction=0.15,
601 wfd_dec_min=-62.5, wfd_dec_max=3.6)
602 result['g'] = generate_goal_map(nside=nside, NES_fraction=0.2,
603 WFD_fraction=0.44, SCP_fraction=0.15,
604 GP_fraction=0.15,
605 wfd_dec_min=-62.5, wfd_dec_max=3.6)
606 result['r'] = generate_goal_map(nside=nside, NES_fraction=0.46,
607 WFD_fraction=1.0, SCP_fraction=0.15,
608 GP_fraction=0.15,
609 wfd_dec_min=-62.5, wfd_dec_max=3.6)
610 result['i'] = generate_goal_map(nside=nside, NES_fraction=0.46,
611 WFD_fraction=1.0, SCP_fraction=0.15,
612 GP_fraction=0.15,
613 wfd_dec_min=-62.5, wfd_dec_max=3.6)
614 result['z'] = generate_goal_map(nside=nside, NES_fraction=0.4,
615 WFD_fraction=0.9, SCP_fraction=0.15,
616 GP_fraction=0.15,
617 wfd_dec_min=-62.5, wfd_dec_max=3.6)
618 result['y'] = generate_goal_map(nside=nside, NES_fraction=0.,
619 WFD_fraction=0.9, SCP_fraction=0.15,
620 GP_fraction=0.15,
621 wfd_dec_min=-62.5, wfd_dec_max=3.6)
622 return result
625def calc_norm_factor(goal_dict, radius=1.75):
626 """Calculate how to normalize a Target_map_basis_function.
627 This is basically:
628 the area of the fov / area of a healpixel /
629 the sum of all of the weighted-healpix values in the footprint.
631 Parameters
632 -----------
633 goal_dict : dict of healpy maps
634 The target goal map(s) being used
635 radius : float (1.75)
636 Radius of the FoV (degrees)
638 Returns
639 -------
640 Value to use as Target_map_basis_function norm_factor kwarg
641 """
642 all_maps_sum = 0
643 for key in goal_dict:
644 good = np.where(goal_dict[key] > 0)
645 all_maps_sum += goal_dict[key][good].sum()
646 nside = hp.npix2nside(goal_dict[key].size)
647 hp_area = hp.nside2pixarea(nside, degrees=True)
648 norm_val = radius**2*np.pi/hp_area/all_maps_sum
649 return norm_val
652def filter_count_ratios(target_maps):
653 """Given the goal maps, compute the ratio of observations we want in each filter.
654 This is basically:
655 per filter, sum the number of pixels in each map and return this per filter value, normalized
656 so that the total sum across all filters is 1.
657 """
658 results = {}
659 all_norm = 0.
660 for key in target_maps:
661 good = target_maps[key] > 0
662 results[key] = np.sum(target_maps[key][good])
663 all_norm += results[key]
664 for key in results:
665 results[key] /= all_norm
666 return results