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']
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)
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 max_coverage = np.max(norm_coverage)
137 if max_coverage != 0:
138 norm_coverage = norm_coverage/max_coverage
139 self.current_footprints = self.footprints * norm_coverage
140 c_sum = np.sum(self.current_footprints)
141 if norm:
142 if c_sum != 0:
143 self.current_footprints = self.current_footprints/c_sum
145 def arr2struc(self, inarr):
146 """take an array and convert it to labled struc array
147 """
148 result = np.empty(self.npix, dtype=self.out_dtype)
149 for key in self.filters:
150 result[key] = inarr[self.filters[key]]
151 # Argle bargel, why doesn't this view work?
152 # struc = inarr.view(dtype=self.out_dtype).squeeze()
153 return result
155 def estimate_counts(self, mjd, nvisits=2.2e6, fov_area=9.6):
156 """Estimate the counts we'll get after some time and visits
157 """
158 pix_area = hp.nside2pixarea(self.nside, degrees=True)
159 pix_per_visit = fov_area/pix_area
160 self._update_mjd(mjd, norm=True)
161 self.estimate = self.current_footprints * pix_per_visit * nvisits
162 return self.arr2struc(self.estimate)
164 def __call__(self, mjd, array=False):
165 """
166 Returns
167 -------
168 a numpy array with the normalized number of observations that should be at each HEALpix.
169 Multiply by the number of HEALpix observations (all filters), to convert to the number of observations
170 desired.
171 """
172 self._update_mjd(mjd)
173 #if array:
174 # return self.current_footprints
175 #else:
176 return self.arr2struc(self.current_footprints)
179class Constant_footprint(Footprint):
180 def __init__(self, nside=32,
181 filters={'u': 0, 'g': 1, 'r': 2, 'i': 3, 'z': 4, 'y': 5}):
182 self.nside = nside
183 self.filters = filters
184 self.npix = hp.nside2npix(nside)
185 self.footprints = np.zeros((len(filters), self.npix), dtype=float)
186 self.out_dtype = list(zip(filters, [float]*len(filters)))
187 self.to_return = self.arr2struc(self.footprints)
189 def __call__(self, mjd, array=False):
190 return self.to_return
194class Footprints(Footprint):
195 """An object to combine multiple Footprint objects.
196 """
197 def __init__(self, footprint_list):
198 self.footprint_list = footprint_list
199 self.mjd_current = None
200 self.current_footprints = 0
201 # Should probably run a check that all the footprints are compatible (same nside, etc)
202 self.npix = footprint_list[0].npix
203 self.out_dtype = footprint_list[0].out_dtype
204 self.filters = footprint_list[0].filters
205 self.nside = footprint_list[0].nside
207 self.footprints = np.zeros((len(self.filters), self.npix), dtype=float)
208 for Fp in self.footprint_list:
209 self.footprints += Fp.footprints
211 def set_footprint(self, filtername, values):
212 pass
214 def _update_mjd(self, mjd, norm=True):
215 if mjd != self.mjd_current:
216 self.mjd_current = mjd
217 self.current_footprints = 0.
218 for fp in self.footprint_list:
219 fp._update_mjd(mjd, norm=False)
220 self.current_footprints += fp.current_footprints
221 c_sum = np.sum(self.current_footprints)
222 if norm:
223 if c_sum != 0:
224 self.current_footprints = self.current_footprints/c_sum
227def ra_dec_hp_map(nside=None):
228 """
229 Return all the RA,dec points for the centers of a healpix map, in radians.
230 """
231 if nside is None:
232 nside = set_default_nside()
233 ra, dec = _hpid2RaDec(nside, np.arange(hp.nside2npix(nside)))
234 return ra, dec
237def get_dustmap(nside=None):
238 if nside is None:
239 nside = set_default_nside()
240 ebvDataDir = getPackageDir('sims_maps')
241 filename = 'DustMaps/dust_nside_%i.npz' % nside
242 dustmap = np.load(os.path.join(ebvDataDir, filename))['ebvMap']
243 return dustmap
246def generate_all_sky(nside=None, elevation_limit=20, mask=hp.UNSEEN):
247 """Set up a healpix map over the entire sky.
248 Calculate RA & Dec, Galactic l & b, Ecliptic l & b, for all healpixels.
249 Calculate max altitude, to set to areas which LSST cannot reach (set these to hp.unseen).
251 This is intended to be a useful tool to use to set up target maps, beyond the standard maps
252 provided in these utilities. Masking based on RA, Dec, Galactic or Ecliptic lat and lon is easier.
254 Parameters
255 ----------
256 nside : int, opt
257 Resolution for the healpix maps.
258 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
259 elevation_limit : float, opt
260 Elevation limit for map.
261 Parts of the sky which do not reach this elevation limit will be set to mask.
262 mask : float, opt
263 Mask value for 'unreachable' parts of the sky, defined as elevation < 20.
264 Note that the actual limits will be set elsewhere, using the observatory model.
265 This limit is for use when understanding what the maps could look like.
267 Returns
268 -------
269 dict of np.ndarray
270 Returns map, RA/Dec, Gal l/b, Ecl l/b (each an np.ndarray IN RADIANS) in a dictionary.
271 """
272 if nside is None:
273 nside = set_default_nside()
275 # Calculate coordinates of everything.
276 skymap = np.zeros(hp.nside2npix(nside), float)
277 ra, dec = ra_dec_hp_map(nside=nside)
278 coord = SkyCoord(ra=ra * u.rad, dec=dec * u.rad, frame='icrs')
279 eclip_lat = coord.barycentrictrueecliptic.lat.deg
280 eclip_lon = coord.barycentrictrueecliptic.lon.deg
281 gal_lon = coord.galactic.l.deg
282 gal_lat = coord.galactic.b.deg
284 # Calculate max altitude (when on meridian).
285 lsst_site = Site('LSST')
286 elev_max = np.pi / 2. - np.abs(dec - lsst_site.latitude_rad)
287 skymap = np.where(int_rounded(elev_max) >= int_rounded(np.radians(elevation_limit), skymap, mask))
289 return {'map': skymap, 'ra': np.degrees(ra), 'dec': np.degrees(dec),
290 'eclip_lat': eclip_lat, 'eclip_lon': eclip_lon,
291 'gal_lat': gal_lat, 'gal_lon': gal_lon}
294def WFD_healpixels(nside=None, dec_min=-62.5, dec_max=3.6):
295 """
296 Define a region based on declination limits only.
298 Parameters
299 ----------
300 nside : int, opt
301 Resolution for the healpix maps.
302 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
303 dec_min : float, opt
304 Minimum declination of the region (deg). Default -62.5.
305 dec_max : float, opt
306 Maximum declination of the region (deg). Default 3.6.
308 Returns
309 -------
310 np.ndarray
311 Healpix map with regions in declination-limited 'wfd' region as 1.
312 """
313 if nside is None:
314 nside = set_default_nside()
316 ra, dec = ra_dec_hp_map(nside=nside)
317 result = np.zeros(ra.size, float)
318 dec = int_rounded(dec)
319 good = np.where((dec >= int_rounded(np.radians(dec_min))) &
320 (dec <= int_rounded(np.radians(dec_max))))
321 result[good] = 1
322 return result
325def WFD_no_gp_healpixels(nside, dec_min=-62.5, dec_max=3.6,
326 center_width=10., end_width=4., gal_long1=290., gal_long2=70.):
327 """
328 Define a wide fast deep region with a galactic plane limit.
330 Parameters
331 ----------
332 nside : int, opt
333 Resolution for the healpix maps.
334 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
335 dec_min : float, opt
336 Minimum declination of the region (deg).
337 dec_max : float, opt
338 Maximum declination of the region (deg).
339 center_width : float, opt
340 Width across the central part of the galactic plane region.
341 end_width : float, opt
342 Width across the remainder of the galactic plane region.
343 gal_long1 : float, opt
344 Longitude at which to start tapering from center_width to end_width.
345 gal_long2 : float, opt
346 Longitude at which to stop tapering from center_width to end_width.
348 Returns
349 -------
350 np.ndarray
351 Healpix map with regions in declination-limited 'wfd' region as 1.
352 """
353 wfd_dec = WFD_healpixels(nside, dec_min=dec_min, dec_max=dec_max)
354 gp = galactic_plane_healpixels(nside=nside, center_width=center_width, end_width=end_width,
355 gal_long1=gal_long1, gal_long2=gal_long2)
356 sky = np.where(wfd_dec - gp > 0, wfd_dec - gp, 0)
357 return sky
360def WFD_bigsky_healpixels(nside):
361 sky = WFD_no_gp_healpixels(nside, dec_min=-72.25, dec_max=12.4, center_width=14.9,
362 gal_long1=0, gal_long2=360)
363 return sky
366def WFD_no_dust_healpixels(nside, dec_min=-72.25, dec_max=12.4, dust_limit=0.19):
367 """Define a WFD region with a dust extinction limit.
369 Parameters
370 ----------
371 nside : int, opt
372 Resolution for the healpix maps.
373 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
374 dec_min : float, opt
375 Minimum dec of the region (deg). Default -72.5 deg.
376 dec_max : float, opt.
377 Maximum dec of the region (deg). Default 12.5 deg.
378 1.75 is the FOV radius in deg.
379 dust_limit : float, None
380 Remove pixels with E(B-V) values greater than dust_limit from the footprint.
382 Returns
383 -------
384 result : numpy array
385 """
386 if nside is None:
387 nside = set_default_nside()
389 ra, dec = ra_dec_hp_map(nside=nside)
390 dustmap = get_dustmap(nside)
392 result = np.zeros(ra.size, float)
393 # First set based on dec range.
394 dec = int_rounded(dec)
395 good = np.where((dec >= int_rounded(np.radians(dec_min))) &
396 (dec <= int_rounded(np.radians(dec_max))))
397 result[good] = 1
398 # Now remove areas with dust extinction beyond the limit.
399 result = np.where(dustmap >= dust_limit, 0, result)
400 return result
403def SCP_healpixels(nside=None, dec_max=-60.):
404 """
405 Define the South Celestial Pole region. Return a healpix map with SCP pixels as 1.
406 """
407 if nside is None:
408 nside = set_default_nside()
410 ra, dec = ra_dec_hp_map(nside=nside)
411 result = np.zeros(ra.size, float)
412 good = np.where(int_rounded(dec) < int_rounded(np.radians(dec_max)))
413 result[good] = 1
414 return result
417def NES_healpixels(nside=None, min_EB=-30.0, max_EB = 10.0, dec_min=2.8):
418 """
419 Define the North Ecliptic Spur region. Return a healpix map with NES pixels as 1.
421 Parameters
422 ----------
423 nside : int
424 A valid healpix nside
425 min_EB : float (-30.)
426 Minimum barycentric true ecliptic latitude (deg)
427 max_EB : float (10.)
428 Maximum barycentric true ecliptic latitude (deg)
429 dec_min : float (2.8)
430 Minimum dec in degrees
432 Returns
433 -------
434 result : numpy array
435 """
436 if nside is None:
437 nside = set_default_nside()
439 ra, dec = ra_dec_hp_map(nside=nside)
440 result = np.zeros(ra.size, float)
441 coord = SkyCoord(ra=ra*u.rad, dec=dec*u.rad)
442 eclip_lat = coord.barycentrictrueecliptic.lat.radian
443 eclip_lat = int_rounded(eclip_lat)
444 dec = int_rounded(dec)
445 good = np.where((eclip_lat > int_rounded(np.radians(min_EB))) &
446 (eclip_lat < int_rounded(np.radians(max_EB))) &
447 (dec > int_rounded(np.radians(dec_min))))
448 result[good] = 1
450 return result
453def galactic_plane_healpixels(nside=None, center_width=10., end_width=4.,
454 gal_long1=290., gal_long2=70.):
455 """
456 Define a Galactic Plane region.
458 Parameters
459 ----------
460 nside : int, opt
461 Resolution for the healpix maps.
462 Default None uses lsst.sims.featureScheduler.utils.set_default_nside to set default (often 32).
463 center_width : float, opt
464 Width at the center of the galactic plane region.
465 end_width : float, opt
466 Width at the remainder of the galactic plane region.
467 gal_long1 : float, opt
468 Longitude at which to start the GP region.
469 gal_long2 : float, opt
470 Longitude at which to stop the GP region.
471 Order matters for gal_long1 / gal_long2!
473 Returns
474 -------
475 np.ndarray
476 Healpix map with galactic plane regions set to 1.
477 """
478 if nside is None:
479 nside = set_default_nside()
480 ra, dec = ra_dec_hp_map(nside=nside)
482 coord = SkyCoord(ra=ra*u.rad, dec=dec*u.rad)
483 gal_lon, gal_lat = coord.galactic.l.deg, coord.galactic.b.deg
484 # Reject anything beyond the central width.
485 sky = np.where(np.abs(gal_lat) < center_width, 1, 0)
486 # Apply the galactic longitude cuts, so that plane goes between gal_long1 to gal_long2.
487 # This is NOT the shortest distance between the angles.
488 gp_length = (gal_long2 - gal_long1) % 360
489 # If the length is greater than 0 then we can add additional cuts.
490 if gp_length > 0:
491 # First, remove anything outside the gal_long1/gal_long2 region.
492 sky = np.where(int_rounded((gal_lon - gal_long1) % 360) < int_rounded(gp_length), sky, 0)
493 # Add the tapers.
494 # These slope from the center (gp_center @ center_width)
495 # to the edge (gp_center + gp_length/2 @ end_width).
496 half_width = gp_length / 2.
497 slope = (center_width - end_width) / half_width
498 gp_center = (gal_long1 + half_width) % 360
499 gp_dist = gal_lon - gp_center
500 gp_dist = np.abs(np.where(int_rounded(gp_dist) > int_rounded(180), (180 - gp_dist) % 180, gp_dist))
501 lat_limit = np.abs(center_width - slope * gp_dist)
502 sky = np.where(int_rounded(np.abs(gal_lat)) < int_rounded(lat_limit), sky, 0)
503 return sky
506def magellanic_clouds_healpixels(nside=None, lmc_radius=10, smc_radius=5):
507 """
508 Define the Galactic Plane region. Return a healpix map with GP pixels as 1.
509 """
510 if nside is None:
511 nside = set_default_nside()
512 ra, dec = ra_dec_hp_map(nside=nside)
513 result = np.zeros(hp.nside2npix(nside))
515 lmc_ra = np.radians(80.893860)
516 lmc_dec = np.radians(-69.756126)
517 lmc_radius = np.radians(lmc_radius)
519 smc_ra = np.radians(13.186588)
520 smc_dec = np.radians(-72.828599)
521 smc_radius = np.radians(smc_radius)
523 dist_to_lmc = _angularSeparation(lmc_ra, lmc_dec, ra, dec)
524 lmc_pix = np.where(int_rounded(dist_to_lmc) < int_rounded(lmc_radius))
525 result[lmc_pix] = 1
527 dist_to_smc = _angularSeparation(smc_ra, smc_dec, ra, dec)
528 smc_pix = np.where(int_rounded(dist_to_smc) < int_rounded(smc_radius))
529 result[smc_pix] = 1
530 return result
533def generate_goal_map(nside=None, NES_fraction = .3, WFD_fraction = 1.,
534 SCP_fraction=0.4, GP_fraction = 0.2,
535 NES_min_EB = -30., NES_max_EB = 10, NES_dec_min = 3.6,
536 SCP_dec_max=-62.5, gp_center_width=10.,
537 gp_end_width=4., gp_long1=290., gp_long2=70.,
538 wfd_dec_min=-62.5, wfd_dec_max=3.6,
539 generate_id_map=False):
540 """
541 Handy function that will put together a target map in the proper order.
542 """
543 if nside is None:
544 nside = set_default_nside()
546 # Note, some regions overlap, thus order regions are added is important.
547 result = np.zeros(hp.nside2npix(nside), dtype=float)
548 id_map = np.zeros(hp.nside2npix(nside), dtype=int)
549 pid = 1
550 prop_name_dict = dict()
552 if NES_fraction > 0.:
553 nes = NES_healpixels(nside=nside, min_EB = NES_min_EB, max_EB = NES_max_EB,
554 dec_min=NES_dec_min)
555 result[np.where(nes != 0)] = 0
556 result += NES_fraction*nes
557 id_map[np.where(nes != 0)] = 1
558 pid += 1
559 prop_name_dict[1] = 'NorthEclipticSpur'
561 if WFD_fraction > 0.:
562 wfd = WFD_healpixels(nside=nside, dec_min=wfd_dec_min, dec_max=wfd_dec_max)
563 result[np.where(wfd != 0)] = 0
564 result += WFD_fraction*wfd
565 id_map[np.where(wfd != 0)] = 3
566 pid += 1
567 prop_name_dict[3] = 'WideFastDeep'
569 if SCP_fraction > 0.:
570 scp = SCP_healpixels(nside=nside, dec_max=SCP_dec_max)
571 result[np.where(scp != 0)] = 0
572 result += SCP_fraction*scp
573 id_map[np.where(scp != 0)] = 2
574 pid += 1
575 prop_name_dict[2] = 'SouthCelestialPole'
577 if GP_fraction > 0.:
578 gp = galactic_plane_healpixels(nside=nside, center_width=gp_center_width,
579 end_width=gp_end_width, gal_long1=gp_long1,
580 gal_long2=gp_long2)
581 result[np.where(gp != 0)] = 0
582 result += GP_fraction*gp
583 id_map[np.where(gp != 0)] = 4
584 pid += 1
585 prop_name_dict[4] = 'GalacticPlane'
587 if generate_id_map:
588 return result, id_map, prop_name_dict
589 else:
590 return result
593def standard_goals(nside=None):
594 """
595 A quick function to generate the "standard" goal maps. This is the traditional WFD/mini survey footprint.
596 """
597 if nside is None:
598 nside = set_default_nside()
600 result = {}
601 result['u'] = generate_goal_map(nside=nside, NES_fraction=0.,
602 WFD_fraction=0.31, SCP_fraction=0.15,
603 GP_fraction=0.15,
604 wfd_dec_min=-62.5, wfd_dec_max=3.6)
605 result['g'] = generate_goal_map(nside=nside, NES_fraction=0.2,
606 WFD_fraction=0.44, SCP_fraction=0.15,
607 GP_fraction=0.15,
608 wfd_dec_min=-62.5, wfd_dec_max=3.6)
609 result['r'] = generate_goal_map(nside=nside, NES_fraction=0.46,
610 WFD_fraction=1.0, SCP_fraction=0.15,
611 GP_fraction=0.15,
612 wfd_dec_min=-62.5, wfd_dec_max=3.6)
613 result['i'] = generate_goal_map(nside=nside, NES_fraction=0.46,
614 WFD_fraction=1.0, SCP_fraction=0.15,
615 GP_fraction=0.15,
616 wfd_dec_min=-62.5, wfd_dec_max=3.6)
617 result['z'] = generate_goal_map(nside=nside, NES_fraction=0.4,
618 WFD_fraction=0.9, SCP_fraction=0.15,
619 GP_fraction=0.15,
620 wfd_dec_min=-62.5, wfd_dec_max=3.6)
621 result['y'] = generate_goal_map(nside=nside, NES_fraction=0.,
622 WFD_fraction=0.9, SCP_fraction=0.15,
623 GP_fraction=0.15,
624 wfd_dec_min=-62.5, wfd_dec_max=3.6)
625 return result
628def calc_norm_factor(goal_dict, radius=1.75):
629 """Calculate how to normalize a Target_map_basis_function.
630 This is basically:
631 the area of the fov / area of a healpixel /
632 the sum of all of the weighted-healpix values in the footprint.
634 Parameters
635 -----------
636 goal_dict : dict of healpy maps
637 The target goal map(s) being used
638 radius : float (1.75)
639 Radius of the FoV (degrees)
641 Returns
642 -------
643 Value to use as Target_map_basis_function norm_factor kwarg
644 """
645 all_maps_sum = 0
646 for key in goal_dict:
647 good = np.where(goal_dict[key] > 0)
648 all_maps_sum += goal_dict[key][good].sum()
649 nside = hp.npix2nside(goal_dict[key].size)
650 hp_area = hp.nside2pixarea(nside, degrees=True)
651 norm_val = radius**2*np.pi/hp_area/all_maps_sum
652 return norm_val
655def filter_count_ratios(target_maps):
656 """Given the goal maps, compute the ratio of observations we want in each filter.
657 This is basically:
658 per filter, sum the number of pixels in each map and return this per filter value, normalized
659 so that the total sum across all filters is 1.
660 """
661 results = {}
662 all_norm = 0.
663 for key in target_maps:
664 good = target_maps[key] > 0
665 results[key] = np.sum(target_maps[key][good])
666 all_norm += results[key]
667 for key in results:
668 results[key] /= all_norm
669 return results