litebird_sim 0.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. default_imo/schema.json.gz +0 -0
  2. images/logo.png +0 -0
  3. litebird_sim/__init__.py +287 -0
  4. litebird_sim/bandpasses.py +344 -0
  5. litebird_sim/compress.py +41 -0
  6. litebird_sim/constants.py +14 -0
  7. litebird_sim/coordinates.py +314 -0
  8. litebird_sim/datautils/Cls_Planck2018_for_PTEP_2020_r0.fits +0 -0
  9. litebird_sim/datautils/Cls_Planck2018_for_PTEP_2020_tensor_r1.fits +0 -0
  10. litebird_sim/detectors.py +435 -0
  11. litebird_sim/dipole.py +308 -0
  12. litebird_sim/distribute.py +218 -0
  13. litebird_sim/gaindrifts.py +600 -0
  14. litebird_sim/healpix.py +395 -0
  15. litebird_sim/hwp.py +145 -0
  16. litebird_sim/hwp_sys/__init__.py +1 -0
  17. litebird_sim/hwp_sys/bandpass_template_module.py +218 -0
  18. litebird_sim/hwp_sys/examples/HWP_systematics.txt +4 -0
  19. litebird_sim/hwp_sys/examples/MFT_100_h_beta_z.txt +23 -0
  20. litebird_sim/hwp_sys/examples/simple_scan.ipynb +1648 -0
  21. litebird_sim/hwp_sys/hwp_sys.py +1392 -0
  22. litebird_sim/imo/__init__.py +12 -0
  23. litebird_sim/imo/imo.py +178 -0
  24. litebird_sim/imobrowser.py +360 -0
  25. litebird_sim/install_imo.py +278 -0
  26. litebird_sim/io.py +646 -0
  27. litebird_sim/madam.py +461 -0
  28. litebird_sim/mapmaking/__init__.py +32 -0
  29. litebird_sim/mapmaking/binner.py +396 -0
  30. litebird_sim/mapmaking/common.py +567 -0
  31. litebird_sim/mapmaking/destriper.py +2078 -0
  32. litebird_sim/mbs/__init__.py +3 -0
  33. litebird_sim/mbs/fg_models/pysm_ame_1.cfg +21 -0
  34. litebird_sim/mbs/fg_models/pysm_dust_0.cfg +16 -0
  35. litebird_sim/mbs/fg_models/pysm_dust_1.cfg +16 -0
  36. litebird_sim/mbs/fg_models/pysm_dust_4.cfg +32 -0
  37. litebird_sim/mbs/fg_models/pysm_dust_5.cfg +23 -0
  38. litebird_sim/mbs/fg_models/pysm_dust_7.cfg +23 -0
  39. litebird_sim/mbs/fg_models/pysm_dust_8.cfg +24 -0
  40. litebird_sim/mbs/fg_models/pysm_freefree_1.cfg +9 -0
  41. litebird_sim/mbs/fg_models/pysm_synch_0.cfg +14 -0
  42. litebird_sim/mbs/fg_models/pysm_synch_1.cfg +14 -0
  43. litebird_sim/mbs/mbs.py +1016 -0
  44. litebird_sim/mpi.py +62 -0
  45. litebird_sim/noise.py +248 -0
  46. litebird_sim/observations.py +708 -0
  47. litebird_sim/pointings.py +221 -0
  48. litebird_sim/quaternions.py +404 -0
  49. litebird_sim/scan_map.py +217 -0
  50. litebird_sim/scanning.py +904 -0
  51. litebird_sim/simulations.py +1779 -0
  52. litebird_sim/spacecraft.py +448 -0
  53. litebird_sim/toast_destriper/__init__.py +432 -0
  54. litebird_sim/version.py +4 -0
  55. litebird_sim-0.12.0.dist-info/LICENSE +674 -0
  56. litebird_sim-0.12.0.dist-info/METADATA +214 -0
  57. litebird_sim-0.12.0.dist-info/RECORD +73 -0
  58. litebird_sim-0.12.0.dist-info/WHEEL +4 -0
  59. static/report_template.html +47 -0
  60. static/sakura.css +165 -0
  61. templates/madam_parameter_file.txt +44 -0
  62. templates/madam_simulation_file.txt +38 -0
  63. templates/report_appendix.md +66 -0
  64. templates/report_binned_map.md +6 -0
  65. templates/report_binned_map_splits.md +8 -0
  66. templates/report_destriper.md +30 -0
  67. templates/report_dipole.md +9 -0
  68. templates/report_gaindrift.md +25 -0
  69. templates/report_header.md +14 -0
  70. templates/report_noise.md +3 -0
  71. templates/report_pointings.md +8 -0
  72. templates/report_quaternions.md +7 -0
  73. templates/report_scan_map.md +8 -0
Binary file
images/logo.png ADDED
Binary file
@@ -0,0 +1,287 @@
1
+ # -*- encoding: utf-8 -*-
2
+ import importlib
3
+ from pathlib import Path
4
+ import numba
5
+
6
+ from .compress import (
7
+ rle_compress,
8
+ rle_decompress,
9
+ )
10
+ from .distribute import distribute_evenly, distribute_optimally
11
+ from .detectors import (
12
+ DetectorInfo,
13
+ FreqChannelInfo,
14
+ InstrumentInfo,
15
+ detector_list_from_parameters,
16
+ )
17
+ from .bandpasses import BandPassInfo
18
+ from .healpix import (
19
+ nside_to_npix,
20
+ npix_to_nside,
21
+ is_npix_ok,
22
+ map_type,
23
+ get_pixel_format,
24
+ write_healpix_map_to_hdu,
25
+ write_healpix_map_to_file,
26
+ )
27
+ from .hwp import (
28
+ HWP,
29
+ IdealHWP,
30
+ )
31
+ from .hwp_sys.hwp_sys import (
32
+ HwpSys,
33
+ )
34
+ from .imo import (
35
+ Imo,
36
+ FormatSpecification,
37
+ Entity,
38
+ Quantity,
39
+ Release,
40
+ )
41
+ from .madam import save_simulation_for_madam
42
+ from .mbs.mbs import Mbs, MbsParameters, MbsSavedMapInfo
43
+ from .mpi import MPI_COMM_WORLD, MPI_ENABLED, MPI_CONFIGURATION
44
+ from .observations import Observation, TodDescription
45
+ from .pointings import (
46
+ apply_hwp_to_obs,
47
+ get_pointing_buffer_shape,
48
+ get_pointings,
49
+ get_pointings_for_observations,
50
+ )
51
+ from .quaternions import (
52
+ quat_rotation_x,
53
+ quat_rotation_y,
54
+ quat_rotation_z,
55
+ quat_right_multiply,
56
+ quat_left_multiply,
57
+ rotate_vector,
58
+ rotate_x_vector,
59
+ rotate_y_vector,
60
+ rotate_z_vector,
61
+ all_rotate_vectors,
62
+ all_rotate_x_vectors,
63
+ all_rotate_y_vectors,
64
+ all_rotate_z_vectors,
65
+ )
66
+ from .scanning import (
67
+ compute_pointing_and_orientation,
68
+ all_compute_pointing_and_orientation,
69
+ spin_to_ecliptic,
70
+ all_spin_to_ecliptic,
71
+ calculate_sun_earth_angles_rad,
72
+ Spin2EclipticQuaternions,
73
+ ScanningStrategy,
74
+ SpinningScanningStrategy,
75
+ get_det2ecl_quaternions,
76
+ get_ecl2det_quaternions,
77
+ )
78
+ from litebird_sim.mapmaking import (
79
+ make_binned_map,
80
+ check_valid_splits,
81
+ BinnerResult,
82
+ make_destriped_map,
83
+ DestriperParameters,
84
+ DestriperResult,
85
+ ExternalDestriperParameters,
86
+ )
87
+ from .simulations import (
88
+ NUMBA_NUM_THREADS_ENVVAR,
89
+ Simulation,
90
+ MpiObservationDescr,
91
+ MpiProcessDescr,
92
+ MpiDistributionDescr,
93
+ )
94
+ from .noise import (
95
+ add_white_noise,
96
+ add_one_over_f_noise,
97
+ add_noise,
98
+ add_noise_to_observations,
99
+ )
100
+ from .scan_map import scan_map, scan_map_in_observations
101
+ from .coordinates import (
102
+ DEFAULT_COORDINATE_SYSTEM,
103
+ DEFAULT_TIME_SCALE,
104
+ ECL_TO_GAL_ROT_MATRIX,
105
+ CoordinateSystem,
106
+ coord_sys_to_healpix_string,
107
+ )
108
+
109
+ from .spacecraft import (
110
+ compute_l2_pos_and_vel,
111
+ compute_lissajous_pos_and_vel,
112
+ spacecraft_pos_and_vel,
113
+ SpacecraftOrbit,
114
+ SpacecraftPositionAndVelocity,
115
+ )
116
+ from .dipole import add_dipole, add_dipole_to_observations, DipoleType
117
+ from .io import (
118
+ write_list_of_observations,
119
+ write_observations,
120
+ read_list_of_observations,
121
+ read_observations,
122
+ )
123
+
124
+ from .gaindrifts import (
125
+ GainDriftType,
126
+ SamplingDist,
127
+ GainDriftParams,
128
+ apply_gaindrift_for_one_detector,
129
+ apply_gaindrift_to_tod,
130
+ apply_gaindrift_to_observations,
131
+ )
132
+
133
+ from .version import __author__, __version__
134
+
135
+
136
+ # Check if the TOAST2 mapmaker is available
137
+ TOAST_ENABLED = importlib.util.find_spec("OpMapMaker", "toast.todmap") is not None
138
+ if not TOAST_ENABLED:
139
+
140
+ def destripe_with_toast2(*args, **kwargs):
141
+ raise ImportError(
142
+ "Install the toast package using `pip` to use destripe_with_toast2"
143
+ )
144
+
145
+ TOAST_ENABLED = False
146
+
147
+
148
+ # Privilege TBB over OpenPM and the internal Numba implementation of a
149
+ # work queue
150
+ numba.config.THREADING_LAYER_CONFIG = ["tbb", "omp", "workqueue"]
151
+
152
+ PTEP_IMO_LOCATION = Path(__file__).parent.parent / "default_imo"
153
+
154
+ __all__ = [
155
+ "__author__",
156
+ "__version__",
157
+ "PTEP_IMO_LOCATION",
158
+ # compress.py
159
+ "rle_compress",
160
+ "rle_decompress",
161
+ # bandpasses.py
162
+ "BandPassInfo",
163
+ # healpix.py
164
+ "nside_to_npix",
165
+ "npix_to_nside",
166
+ "is_npix_ok",
167
+ "map_type",
168
+ "get_pixel_format",
169
+ "write_healpix_map_to_hdu",
170
+ "write_healpix_map_to_file",
171
+ # distribute.py
172
+ "distribute_evenly",
173
+ "distribute_optimally",
174
+ # imo.py
175
+ "Imo",
176
+ # imoobjects
177
+ "FormatSpecification",
178
+ "Entity",
179
+ "Quantity",
180
+ "Release",
181
+ # detectors.py
182
+ "DetectorInfo",
183
+ "FreqChannelInfo",
184
+ "InstrumentInfo",
185
+ "detector_list_from_parameters",
186
+ # hwp.py
187
+ "HWP",
188
+ "IdealHWP",
189
+ # hwp_sys/hwp_sys.py
190
+ "HwpSys",
191
+ # madam.py
192
+ "save_simulation_for_madam",
193
+ # mbs.py
194
+ "Mbs",
195
+ "MbsParameters",
196
+ "MbsSavedMapInfo",
197
+ # mpi.py
198
+ "MPI_COMM_WORLD",
199
+ "MPI_ENABLED",
200
+ "MPI_CONFIGURATION",
201
+ # observations.py
202
+ "Observation",
203
+ "TodDescription",
204
+ # quaternions.py
205
+ "quat_rotation_x",
206
+ "quat_rotation_y",
207
+ "quat_rotation_z",
208
+ "quat_right_multiply",
209
+ "quat_left_multiply",
210
+ "rotate_vector",
211
+ "rotate_x_vector",
212
+ "rotate_y_vector",
213
+ "rotate_z_vector",
214
+ "all_rotate_vectors",
215
+ "all_rotate_x_vectors",
216
+ "all_rotate_y_vectors",
217
+ "all_rotate_z_vectors",
218
+ # scanning.py
219
+ "compute_pointing_and_orientation",
220
+ "all_compute_pointing_and_orientation",
221
+ "spin_to_ecliptic",
222
+ "all_spin_to_ecliptic",
223
+ "calculate_sun_earth_angles_rad",
224
+ "Spin2EclipticQuaternions",
225
+ "ScanningStrategy",
226
+ "SpinningScanningStrategy",
227
+ "get_det2ecl_quaternions",
228
+ "get_ecl2det_quaternions",
229
+ # pointings.py
230
+ "apply_hwp_to_obs",
231
+ "get_pointing_buffer_shape",
232
+ "get_pointings",
233
+ "get_pointings_for_observations",
234
+ # mapmaking
235
+ "make_binned_map",
236
+ "check_valid_splits",
237
+ "BinnerResult",
238
+ "make_destriped_map",
239
+ "DestriperParameters",
240
+ "DestriperResult",
241
+ "ExternalDestriperParameters",
242
+ # toast_destriper.py
243
+ "TOAST_ENABLED",
244
+ "destripe_with_toast2",
245
+ # simulations.py
246
+ "NUMBA_NUM_THREADS_ENVVAR",
247
+ "Simulation",
248
+ "MpiObservationDescr",
249
+ "MpiProcessDescr",
250
+ "MpiDistributionDescr",
251
+ # noise.py
252
+ "add_white_noise",
253
+ "add_one_over_f_noise",
254
+ "add_noise",
255
+ "add_noise_to_observations",
256
+ # scan_map.py
257
+ "scan_map",
258
+ "scan_map_in_observations",
259
+ # dipole.py
260
+ "add_dipole",
261
+ "add_dipole_to_observations",
262
+ "DipoleType",
263
+ # coordinates.py
264
+ "DEFAULT_COORDINATE_SYSTEM",
265
+ "DEFAULT_TIME_SCALE",
266
+ "ECL_TO_GAL_ROT_MATRIX",
267
+ "CoordinateSystem",
268
+ "coord_sys_to_healpix_string",
269
+ # spacecraft.py
270
+ "compute_l2_pos_and_vel",
271
+ "compute_lissajous_pos_and_vel",
272
+ "spacecraft_pos_and_vel",
273
+ "SpacecraftOrbit",
274
+ "SpacecraftPositionAndVelocity",
275
+ # io
276
+ "write_list_of_observations",
277
+ "write_observations",
278
+ "read_list_of_observations",
279
+ "read_observations",
280
+ # gaindrifts.py
281
+ "GainDriftType",
282
+ "SamplingDist",
283
+ "GainDriftParams",
284
+ "apply_gaindrift_for_one_detector",
285
+ "apply_gaindrift_to_tod",
286
+ "apply_gaindrift_to_observations",
287
+ ]
@@ -0,0 +1,344 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import numpy as np
6
+ import scipy as sp
7
+ import logging
8
+ from typing import Optional, Union
9
+ from uuid import UUID
10
+
11
+ from .imo import Imo
12
+
13
+
14
+ def _apod(x, a, b):
15
+ return (1 + np.cos((x - a) / (b - a) * np.pi)) / 2
16
+
17
+
18
+ @dataclass
19
+ class BandPassInfo:
20
+ """A class wrapping the basic information about a detector bandpass.
21
+
22
+ This class encodes the basic properties of a frequency band.
23
+
24
+ It can be initialized in three ways:
25
+
26
+ - The default constructor will generate top-hat bandpasses,
27
+ assuming that band centroid and width are provided.
28
+
29
+ - Through the class method :meth:`.from_imo`, which reads the
30
+ detector bandpass from the LiteBIRD Instrument Model (see
31
+ the :class:`.Imo` class).
32
+
33
+ Args:
34
+
35
+ - bandcenter_ghz (float): center frequency in GHz
36
+
37
+ - bandwidth_ghz (float): width of the band (default=0.)
38
+
39
+ - bandlow_ghz (float): lowest frequency to sample the band,
40
+ i.e. the first element of the frequency array considered
41
+ (default: bandcenter_ghz - bandwidth_ghz)
42
+
43
+ - bandhigh_ghz (float): highest frequency to sample the band,
44
+ i.e. the last element of frequency array considered
45
+ (default: bandcenter_ghz + bandwidth_ghz)
46
+
47
+ - nsamples_inband (int): number of elements to sample the band
48
+ (default=128)
49
+
50
+ - name (str) : ID of the band
51
+
52
+ - normalize(bool) : If set to true bandpass weights will be
53
+ normalized to 1
54
+
55
+ - bandtype (str): a string describing the band profile. It can be
56
+ one of the following:
57
+
58
+ - ``top-hat`` (default)
59
+
60
+ - ``top-hat-exp``: the edges of the band are apodized with an
61
+ exponential profile
62
+
63
+ - ``top-hat-cosine``: the edges of the band are apodized with
64
+ a cosine profile
65
+
66
+ - ``cheby``: the bandpass encodes a Chebyshev profile
67
+
68
+ - alpha_exp (float): out-of-band exponential decay index for
69
+ low freq edge.
70
+
71
+ - beta_exp (float) : out-of-band exponential decay index for
72
+ high freq edge
73
+
74
+ - cosine_apo_length (int): the numerical factor related to
75
+ the cosine apodization length
76
+
77
+ - cheby_poly_order (int): chebyshev filter order.
78
+
79
+ - cheby_ripple_dB (int): maximum ripple amplitude in decibels.
80
+ """
81
+
82
+ bandcenter_ghz: float = 0.0
83
+ bandwidth_ghz: float = 0.0
84
+ bandlow_ghz: Union[float, None] = None
85
+ bandhigh_ghz: Union[float, None] = None
86
+ nsamples_inband: int = 128
87
+ name: str = ""
88
+ bandtype: str = "top-hat"
89
+ normalize: bool = False
90
+ model: str = None
91
+
92
+ alpha_exp: float = 1
93
+ beta_exp: float = 1
94
+ cosine_apo_length: float = 5
95
+ cheby_poly_order: int = 3
96
+ cheby_ripple_dB: int = 3
97
+
98
+ def __post_init__(self):
99
+ """
100
+ Constructor of the class
101
+
102
+ """
103
+ if self.bandcenter_ghz <= 0.0:
104
+ raise ValueError(
105
+ "The band center must be strictly positive, \
106
+ assign a value to bandcenter_ghz > 0.0"
107
+ )
108
+ self.f0, self.f1 = self.get_edges()
109
+ # we extend the wings out-of-band of the top-hat bandpass
110
+ # before and after the edges
111
+ if not self.bandlow_ghz:
112
+ self.bandlow_ghz = self.bandcenter_ghz - self.bandwidth_ghz
113
+ if not self.bandhigh_ghz:
114
+ self.bandhigh_ghz = self.bandcenter_ghz + self.bandwidth_ghz
115
+ self.freqs_ghz = np.linspace(
116
+ self.bandlow_ghz, self.bandhigh_ghz, self.nsamples_inband
117
+ )
118
+ # checking that the bandpass edges lie within the freq range
119
+ if self.bandlow_ghz > self.f0 or self.bandhigh_ghz < self.f1 - 1:
120
+ raise ValueError("The bandpass is out of the frequency range")
121
+
122
+ self.isnormalized = False
123
+ if self.bandtype == "top-hat":
124
+ self._get_top_hat_bandpass(normalize=self.normalize)
125
+ elif self.bandtype == "top-hat-cosine":
126
+ self._get_top_hat_bandpass(apodization="cosine", normalize=self.normalize)
127
+ elif self.bandtype == "top-hat-exp":
128
+ self._get_top_hat_bandpass(apodization="exp", normalize=self.normalize)
129
+ elif self.bandtype == "cheby":
130
+ self._get_chebyshev_bandpass(normalize=self.normalize)
131
+ else:
132
+ logging.warning(f"{self.bandtype} profile not implemented. Assume top-hat")
133
+ self._get_top_hat_bandpass(normalize=self.normalize)
134
+
135
+ def get_edges(self):
136
+ """
137
+ get the edges of the tophat band
138
+ """
139
+ return (
140
+ self.bandcenter_ghz - self.bandwidth_ghz / 2,
141
+ self.bandcenter_ghz + self.bandwidth_ghz / 2,
142
+ )
143
+
144
+ def _get_top_hat_bandpass(self, normalize=False, apodization: Optional[str] = None):
145
+ """
146
+ Sample a top-hat bandpass, given the centroid and the bandwidth.
147
+ If the `normalize` flag is set to ``True``, the transmission
148
+ coefficients are
149
+ normalized so that its integral is 1 over the frequency band.
150
+ The parameter `apodization` must be either ``"cosine"``, ``"exp"``,
151
+ or ``None``: in the latter case, no apodization of the beamshape
152
+ is performed.
153
+ """
154
+
155
+ self.weights = np.zeros_like(self.freqs_ghz)
156
+ mask = np.ma.masked_inside(self.freqs_ghz, self.f0, self.f1).mask
157
+ self.weights[mask] = 1.0
158
+
159
+ if apodization == "cosine":
160
+ self._cosine_apodize_bandpass()
161
+ elif apodization == "exp":
162
+ self._exp_apodize_bandpass()
163
+
164
+ if normalize:
165
+ self.normalize_band()
166
+
167
+ def normalize_band(self):
168
+ """
169
+ Normalize the band transmission coefficients
170
+
171
+ """
172
+ A = np.trapz(self.weights, self.freqs_ghz)
173
+ self.weights /= A
174
+ self.isnormalized = True
175
+
176
+ def _exp_apodize_bandpass(self):
177
+ """Define a bandpass with exponential tails and unit transmission
178
+ in band freqs: frequency in GHz
179
+ """
180
+ mask_beta = np.ma.masked_greater(self.freqs_ghz, self.f1).mask
181
+ self.weights[mask_beta] = np.exp(
182
+ -self.beta_exp * (self.freqs_ghz[mask_beta] - self.f1)
183
+ )
184
+ mask_alpha = np.ma.masked_less(self.freqs_ghz, self.f0).mask
185
+ self.weights[mask_alpha] = np.exp(
186
+ self.alpha_exp * (self.freqs_ghz[mask_alpha] - self.f0)
187
+ )
188
+
189
+ def _cosine_apodize_bandpass(self):
190
+ """
191
+ Define a bandpass with cosine tails and unit transmission in band
192
+ """
193
+
194
+ apolength = self.bandwidth_ghz / self.cosine_apo_length
195
+ f_above = self.bandcenter_ghz * (1 + self.bandwidth_ghz / 2 + apolength)
196
+ f_below = self.bandcenter_ghz * (1 - self.bandwidth_ghz / 2 - apolength)
197
+ mask_above = np.ma.masked_inside(self.freqs_ghz, self.f1, f_above).mask
198
+
199
+ x_ab = np.linspace(self.f1, f_above, self.freqs_ghz[mask_above].size)
200
+
201
+ self.weights[mask_above] = _apod(x_ab, self.f1, f_above)
202
+
203
+ mask_below = np.ma.masked_inside(self.freqs_ghz, f_below, self.f0).mask
204
+ x_bel = np.linspace(f_below, self.f0, self.freqs_ghz[mask_below].size)
205
+ self.weights[mask_below] = _apod(x_bel, self.f0, f_below)
206
+
207
+ # Chebyshev profile bandpass
208
+ def _get_chebyshev_bandpass(self, normalize=False):
209
+ """
210
+ Define a bandpass with chebyshev prototype.
211
+ """
212
+ b, a = sp.signal.cheby1(
213
+ self.cheby_poly_order,
214
+ self.cheby_ripple_dB,
215
+ [2.0 * np.pi * self.f0 * 1e9, 2.0 * np.pi * self.f1 * 1e9],
216
+ "bandpass",
217
+ analog=True,
218
+ )
219
+ w, h = sp.signal.freqs(b, a, worN=self.freqs_ghz * 2 * np.pi * 1e9)
220
+
221
+ self.weights = abs(h)
222
+ if normalize:
223
+ A = self.get_normalization()
224
+ self.weights /= A
225
+ self.isnormalized = True
226
+
227
+ def get_normalization(self):
228
+ """
229
+ Estimate the integral over the frequency band
230
+ """
231
+ return np.trapz(self.weights, self.freqs_ghz)
232
+
233
+ # Find effective central frequency of a bandpass profile
234
+ def find_central_frequency(self):
235
+ """Find the effective central frequency of
236
+ a bandpass profile as defined in https://arxiv.org/abs/1303.5070
237
+ """
238
+ if self.isnormalized:
239
+ return np.trapz(self.freqs_ghz * self.weights, self.freqs_ghz)
240
+ else:
241
+ return (
242
+ np.trapz(self.freqs_ghz * self.weights, self.freqs_ghz)
243
+ / self.get_normalization()
244
+ )
245
+
246
+ @staticmethod
247
+ def from_imo(imo: Imo, url: Union[UUID, str]):
248
+ """Create a `BandPassInfo` object from a definition in the IMO
249
+ The `url` must either specify a UUID or a full URL to the
250
+ object.
251
+
252
+ """
253
+ obj = imo.query(url)
254
+ return BandPassInfo.from_dict(obj.metadata)
255
+
256
+ def _interpolate_band(self):
257
+ """
258
+ This function aims at building the sampler in order to
259
+ generate random samples
260
+ statistically equivalent to the model bandpass
261
+ """
262
+ # normalize band
263
+
264
+ if not self.isnormalized:
265
+ self.normalize_band()
266
+ # Interpolate the band
267
+ b = sp.interpolate.interp1d(x=self.freqs_ghz, y=self.weights)
268
+ # estimate the CDF
269
+ Pnu = np.array(
270
+ [
271
+ sp.integrate.quad(b, a=self.freqs_ghz.min(), b=inu)[0]
272
+ for inu in self.freqs_ghz[1:]
273
+ ]
274
+ )
275
+ # interpolate the inverse CDF
276
+ self.Sampler = sp.interpolate.interp1d(
277
+ Pnu,
278
+ self.freqs_ghz[:-1] + np.diff(self.freqs_ghz),
279
+ bounds_error=False,
280
+ fill_value="extrapolate",
281
+ )
282
+
283
+ def bandpass_resampling(self, bstrap_size=1000, nresample=54, model=None):
284
+ """
285
+ Resample a bandpass with bootstrap resampling.
286
+
287
+ Note that the user can provide any sampler built with the
288
+ `interpolate_band` method; otherwise, an error will be raised!
289
+
290
+ This function should be used when the user wants to generate
291
+ many realizations of bandpasses, e.g. per detector bands.
292
+ There is no need to initialize many
293
+ instances of the class :class:`.BandPassInfo`, just rerun this
294
+ functions multiple times issuing the same bandpass model instance.
295
+
296
+ Args :
297
+
298
+ - `bstrap_size` (int): encodes the size of the random dataset
299
+ to be generated from the Sampler
300
+
301
+ - `nresample` (int): define how fine is the grid for the
302
+ resampled bandpass
303
+
304
+ - `model` (BandPassInfo.model): We can resample from a model previously
305
+ constructed with this function. The default value is set to ``None``:
306
+ in this case, it initializes the bandpass sampler with the model set
307
+ in the class instance (recommended use).
308
+ """
309
+
310
+ if model is not None:
311
+ logging.info(f"Bandpass sampler from {model.name }")
312
+ sampler = model.Sampler
313
+ else:
314
+ try:
315
+ sampler = self.Sampler
316
+ except AttributeError:
317
+ print(
318
+ "Can't resample if no sampler is built and/or provided, "
319
+ "initializing the sampler and interpolating the band"
320
+ )
321
+ self._interpolate_band()
322
+ sampler = self.Sampler
323
+ except AttributeError:
324
+ logging.warning(
325
+ "Can't resample if no sampler is built and/or provided, "
326
+ "initializing the sampler and interpolating the band"
327
+ )
328
+ self._interpolate_band()
329
+ sampler = self.Sampler
330
+
331
+ X = np.random.uniform(size=bstrap_size)
332
+ bins_nu = np.linspace(self.freqs_ghz.min(), self.freqs_ghz.max(), nresample)
333
+ h, xb = np.histogram(sampler(X), density=True, bins=bins_nu)
334
+
335
+ nu_b = xb[:-1] + np.diff(xb)
336
+ resampled_bpass = abs(
337
+ sp.interpolate.interp1d(
338
+ nu_b, h, kind="cubic", bounds_error=False, fill_value=0.0
339
+ )(self.freqs_ghz)
340
+ )
341
+ if self.isnormalized:
342
+ return resampled_bpass / np.trapz(resampled_bpass, self.freqs_ghz)
343
+ else:
344
+ return resampled_bpass
@@ -0,0 +1,41 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ import numpy as np
4
+ from numba import njit
5
+
6
+
7
+ def rle_compress(arr):
8
+ """Perform a Run-Length encoding of the input array
9
+
10
+ Returns a NumPy matrix of shape 2×N, where N is an integer; the first
11
+ row contains the *lengths* of each run, and the second row contains
12
+ the *values*."""
13
+
14
+ # This code was adapted from https://stackoverflow.com/a/32681075/3967151
15
+
16
+ ia = np.asarray(arr) # force numpy
17
+ nsamples = len(ia)
18
+ if nsamples == 0:
19
+ return None, None
20
+ else:
21
+ y = ia[1:] != ia[:-1]
22
+ indexes = np.append(np.where(y), nsamples - 1)
23
+ runs = np.diff(np.append(-1, indexes))
24
+ return np.array([runs, ia[indexes]])
25
+
26
+
27
+ @njit
28
+ def _rle_decompress(compressed_arr, result):
29
+ out_idx = 0
30
+ for in_idx in range(compressed_arr.shape[1]):
31
+ length = compressed_arr[0, in_idx]
32
+ val = compressed_arr[1, in_idx]
33
+ for i in range(length):
34
+ result[out_idx] = val
35
+ out_idx += 1
36
+
37
+
38
+ def rle_decompress(compressed_arr):
39
+ result = np.empty(np.sum(compressed_arr[0, :]))
40
+ _rle_decompress(compressed_arr, result)
41
+ return result
@@ -0,0 +1,14 @@
1
+ # -*- encoding: utf-8 -*-
2
+ from astropy.constants import c as c_light
3
+ from astropy.constants import h, k_B
4
+
5
+ C_LIGHT_KM_S = c_light.value / 1e3
6
+ H_OVER_K_B = h.value / k_B.value
7
+
8
+ T_CMB_K = 2.72548 # Fixsen 2009 http://arxiv.org/abs/0911.1955
9
+
10
+ SOLAR_VELOCITY_KM_S = 369.8160
11
+ SOLAR_VELOCITY_GAL_LAT_RAD = 0.842_173_724
12
+ SOLAR_VELOCITY_GAL_LON_RAD = 4.608_035_744_4
13
+
14
+ EARTH_L2_DISTANCE_KM = 1_496_509.30522