ler 0.4.2__py3-none-any.whl → 0.4.3__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.

Potentially problematic release.


This version of ler might be problematic. Click here for more details.

Files changed (35) hide show
  1. ler/__init__.py +26 -26
  2. ler/gw_source_population/__init__.py +1 -0
  3. ler/gw_source_population/cbc_source_parameter_distribution.py +1073 -815
  4. ler/gw_source_population/cbc_source_redshift_distribution.py +618 -294
  5. ler/gw_source_population/jit_functions.py +484 -9
  6. ler/gw_source_population/sfr_with_time_delay.py +107 -0
  7. ler/image_properties/image_properties.py +41 -12
  8. ler/image_properties/multiprocessing_routine.py +5 -209
  9. ler/lens_galaxy_population/__init__.py +2 -0
  10. ler/lens_galaxy_population/epl_shear_cross_section.py +0 -0
  11. ler/lens_galaxy_population/jit_functions.py +101 -9
  12. ler/lens_galaxy_population/lens_galaxy_parameter_distribution.py +813 -881
  13. ler/lens_galaxy_population/lens_param_data/density_profile_slope_sl.txt +5000 -0
  14. ler/lens_galaxy_population/lens_param_data/external_shear_sl.txt +2 -0
  15. ler/lens_galaxy_population/lens_param_data/number_density_zl_zs.txt +48 -0
  16. ler/lens_galaxy_population/lens_param_data/optical_depth_epl_shear_vd_ewoud.txt +48 -0
  17. ler/lens_galaxy_population/mp copy.py +554 -0
  18. ler/lens_galaxy_population/mp.py +736 -138
  19. ler/lens_galaxy_population/optical_depth.py +2248 -616
  20. ler/rates/__init__.py +1 -2
  21. ler/rates/gwrates.py +126 -72
  22. ler/rates/ler.py +218 -111
  23. ler/utils/__init__.py +2 -0
  24. ler/utils/function_interpolation.py +322 -0
  25. ler/utils/gwsnr_training_data_generator.py +233 -0
  26. ler/utils/plots.py +1 -1
  27. ler/utils/test.py +1078 -0
  28. ler/utils/utils.py +492 -125
  29. {ler-0.4.2.dist-info → ler-0.4.3.dist-info}/METADATA +30 -17
  30. ler-0.4.3.dist-info/RECORD +34 -0
  31. {ler-0.4.2.dist-info → ler-0.4.3.dist-info}/WHEEL +1 -1
  32. ler/rates/ler copy.py +0 -2097
  33. ler-0.4.2.dist-info/RECORD +0 -25
  34. {ler-0.4.2.dist-info → ler-0.4.3.dist-info/licenses}/LICENSE +0 -0
  35. {ler-0.4.2.dist-info → ler-0.4.3.dist-info}/top_level.txt +0 -0
@@ -1,485 +1,1354 @@
1
+ import os
1
2
  from numba import njit
2
3
  from multiprocessing import Pool
3
4
  import numpy as np
4
5
  from scipy.integrate import quad
5
- from scipy.stats import gengamma
6
+ from scipy.stats import gengamma, gaussian_kde
7
+ from scipy.interpolate import CubicSpline
6
8
  from astropy.cosmology import LambdaCDM
7
- from ..utils import interpolator_from_pickle, cubic_spline_interpolator, inverse_transform_sampler
8
- from .jit_functions import phi_cut_SIE, axis_ratio_rayleigh, axis_ratio_SIS, phi, phi_loc_bernardi
9
+ from tqdm import tqdm
10
+ from lenstronomy.LensModel.Solver.epl_shear_solver import caustics_epl_shear
11
+ from shapely.geometry import Polygon
12
+
13
+
14
+ from ..utils import cubic_spline_interpolator, inverse_transform_sampler, cubic_spline_interpolator2d_array, save_pickle, interpolator_pickle_path, FunctionConditioning, inverse_transform_sampler2d, pdf_cubic_spline_interpolator2d_array, normal_pdf, normal_pdf_2d, load_txt_from_module
15
+
16
+ from .jit_functions import phi_cut_SIE, phi, phi_loc_bernardi, phi_q2_ellipticity_hemanta, lens_redshift_SDSS_catalogue_sis, axis_ratio_rayleigh_pdf
9
17
 
10
18
  class OpticalDepth():
11
19
  """
12
- Class to calculate the optical depth, velocity dispersion and axis-ratio of a lens galaxy population.
13
-
14
- Parameters
15
- ----------
16
- npool : `int`
17
- number of processors to use for multiprocessing
18
- z_min : `float`
19
- minimum redshift of the lens galaxy population
20
- z_max : `float`
21
- maximum redshift of the lens galaxy population
22
- optical_depth_function : `str` or `callable`
23
- Function or function name to calculate optical depth.
24
- Check for default/available optical depth functions by running,
25
- >>> from ler.lens_galaxy_population import OpticalDepth
26
- >>> print(OpticalDepth().available_optical_depth_list_and_its_params)
27
- sampler_priors, sampler_priors_params : `dict`, `dict`
28
- dictionary of sampler functions and it's parameters to sample velocity dispersion and axis-ratio.
29
- Check for default/available sampler priors and corresponding input parameters by running,
30
- >>> from ler.lens_galaxy_population import OpticalDepth
31
- >>> print(OpticalDepth().available_velocity_dispersion_list_and_its_params)
32
- >>> print(OpticalDepth().available_axis_ratio_list_and_its_params)
33
- cosmology : `astropy.cosmology`
34
- Cosmology to use
35
- default: None/astropy.cosmology.FlatLambdaCDM(H0=70, Om0=0.3)
36
- directory : `str`
37
- directory to store interpolator pickle files
38
- default: "./interpolator_pickle"
39
- create_new_interpolator : `dict`
40
- dictionary to create new interpolator for velocity dispersion and optical depth.
41
-
42
- Examples
43
- --------
44
- >>> from ler.lens_galaxy_population import OpticalDepth
45
- >>> od = OpticalDepth()
46
- >>> print(od.strong_lensing_optical_depth(0.5))
47
-
48
- Instance Attributes
49
- -------------------
50
- OpticalDepth class has the following attributes:
51
- +-------------------------------------+----------------------------------+
52
- | Atrributes | Type |
53
- +=====================================+==================================+
54
- |:attr:`~npool` | `int` |
55
- +-------------------------------------+----------------------------------+
56
- |:attr:`~z_min` | `float` |
57
- +-------------------------------------+----------------------------------+
58
- |:attr:`~z_max` | `float` |
59
- +-------------------------------------+----------------------------------+
60
- |:attr:`~functions` | `dict` |
61
- +-------------------------------------+----------------------------------+
62
- |:attr:`~sampler_priors` | `dict` |
63
- +-------------------------------------+----------------------------------+
64
- |:attr:`~sampler_priors_params` | `dict` |
65
- +-------------------------------------+----------------------------------+
66
- |:attr:`~cosmo` | `astropy.cosmology` |
67
- +-------------------------------------+----------------------------------+
68
- |:attr:`~directory` | `str` |
69
- +-------------------------------------+----------------------------------+
70
- |:attr:`~c_n_i` | `dict` |
71
- +-------------------------------------+----------------------------------+
72
- |:attr:`~vd_inv_cdf` | `numpy.ndarray` |
73
- +-------------------------------------+----------------------------------+
74
- |:attr:`~available_velocity_dispersion_list_and_its_params` |
75
- +-------------------------------------+----------------------------------+
76
- | | `dict` |
77
- +-------------------------------------+----------------------------------+
78
- |:attr:`~available_axis_ratio_list_and_its_params` |
79
- +-------------------------------------+----------------------------------+
80
- | | `dict` |
81
- +-------------------------------------+----------------------------------+
82
- |:attr:`~available_optical_depth_list_and_its_params` |
83
- +-------------------------------------+----------------------------------+
84
- | | `dict` |
85
- +-------------------------------------+----------------------------------+
86
-
87
- Instance Methods
88
- ----------------
89
- OpticalDepth class has the following methods:
90
- +-------------------------------------+----------------------------------+
91
- | Methods | Type |
92
- +=====================================+==================================+
93
- |:meth:`~create_lookup_table` | Function to create a lookup |
94
- | | table for redshift dependent |
95
- | | cosmological functions |
96
- +-------------------------------------+----------------------------------+
97
- |:meth:`~initialize_velocity_dispersion_sampler` |
98
- +-------------------------------------+----------------------------------+
99
- | | Function to initialize velocity |
100
- | | dispersion sampler |
101
- +-------------------------------------+----------------------------------+
102
- |:meth:`~initialize_optical_depth_function` |
103
- +-------------------------------------+----------------------------------+
104
- | | Function to initialize optical |
105
- | | depth function |
106
- +-------------------------------------+----------------------------------+
107
- |:meth:`~velocity_dispersion_gengamma`| Function to sample velocity |
108
- | | dispersion from gengamma |
109
- | | distribution |
110
- +-------------------------------------+----------------------------------+
111
- |:meth:`~velocity_dispersion_bernardi`| Function to sample velocity |
112
- | | dispersion from Bernardi et al. |
113
- | | (2010) |
114
- +-------------------------------------+----------------------------------+
115
- |:meth:`~velocity_dispersion_ewoud` | Function to sample velocity |
116
- | | dispersion from Wempe et al. |
117
- | | (2022) |
118
- +-------------------------------------+----------------------------------+
119
- |:meth:`~cross_section_SIS` | Function to compute the SIS |
120
- | | cross-section |
121
- +-------------------------------------+----------------------------------+
122
- |:meth:`~tau_zl_zs` | Function to compute the optical |
123
- | | depth integrand |
124
- +-------------------------------------+----------------------------------+
125
- |:meth:`~optical_depth_calculator` | Function to compute the optical |
126
- | | depth without multiprocessing |
127
- +-------------------------------------+----------------------------------+
128
- |:meth:`~optical_depth_multiprocessing` |
129
- +-------------------------------------+----------------------------------+
130
- | | Function to compute the optical |
131
- | | depth with multiprocessing |
132
- +-------------------------------------+----------------------------------+
133
- |:meth:`~optical_depth_SIS_haris` | Function to compute the strong |
134
- | | lensing optical depth (SIS) |
135
- | | Haris et al. (2018) (analytic) |
136
- +-------------------------------------+----------------------------------+
137
- |:meth:`~optical_depth_SIS_hemanta` | Function to compute the strong |
138
- | | lensing optical depth (SIS) |
139
- | | (numerical) |
140
- +-------------------------------------+----------------------------------+
141
- |:meth:`~optical_depth_SIE_hemanta` | Function to compute the strong |
142
- | | lensing optical depth (SIE) |
143
- | | (numerical) |
144
- +-------------------------------------+----------------------------------+
145
- |:meth:`~strong_lensing_optical_depth`| Function to compute the strong |
146
- | | lensing optical depth |
147
- +-------------------------------------+----------------------------------+
148
- |:meth:`~sample_velocity_dispersion` | Function to sample velocity |
149
- | | dispersion |
150
- +-------------------------------------+----------------------------------+
151
- |:meth:`~sample_axis_ratio` | Function to sample axis ratio |
152
- +-------------------------------------+----------------------------------+
20
+ Class to calculate the optical depth, velocity dispersion and axis-ratio of a lens galaxy population.
21
+
22
+ Parameters
23
+ ----------
24
+ npool : int, optional
25
+ Number of processors to use for multiprocessing (default is 4).
26
+ z_min : float, optional
27
+ Minimum redshift of the lens galaxy population (default is 0.0).
28
+ z_max : float, optional
29
+ Maximum redshift of the lens galaxy population (default is 10.0).
30
+ cosmology : astropy.cosmology, optional
31
+ Cosmology object to use (default is FlatLambdaCDM with H0=70, Om0=0.3, Ode0=0.7).
32
+ lens_type : str, optional
33
+ Type of the lens galaxy. Must be one of ['sie_galaxy', 'epl_shear_galaxy', 'sis_galaxy'] (default is 'epl_shear_galaxy').
34
+ lens_functions : dict, optional
35
+ Dictionary with lens-related functions.
36
+ lens_functions_params : dict, optional
37
+ Dictionary with parameters for the lens-related functions.
38
+ lens_param_samplers : dict, optional
39
+ Dictionary of sampler functions for velocity dispersion and axis-ratio.
40
+ lens_param_samplers_params : dict, optional
41
+ Dictionary with parameters for the priors of the samplers.
42
+ directory : str, optional
43
+ Directory where the interpolators are saved (default is './interpolator_pickle').
44
+ If True, creates a new interpolator (default is False).
45
+ verbose : bool, optional
46
+ If True, prints additional information during initialization (default is False).
47
+
48
+ Raises
49
+ ------
50
+ ValueError
51
+ If `lens_type` is not in ['sie_galaxy', 'epl_shear_galaxy', 'sis_galaxy'].
153
52
  """
154
53
 
155
54
  def __init__(self,
156
55
  npool=4,
157
- z_min=0.001,
56
+ z_min=0.0,
158
57
  z_max=10.0,
159
- optical_depth_function=None,
160
- sampler_priors=None,
161
- sampler_priors_params=None,
162
58
  cosmology=None,
59
+ lens_type="epl_shear_galaxy",
60
+ lens_functions= None,
61
+ lens_functions_params=None,
62
+ lens_param_samplers=None,
63
+ lens_param_samplers_params=None,
163
64
  directory="./interpolator_pickle",
164
65
  create_new_interpolator=False,
66
+ verbose=False,
165
67
  ):
166
-
68
+
69
+ print("\nInitializing OpticalDepth class\n")
70
+ if lens_type not in ["sie_galaxy", "epl_shear_galaxy", "sis_galaxy"]:
71
+ raise ValueError("lens_type not in ['sie_galaxy', 'epl_shear_galaxy', 'sis_galaxy']")
72
+ self.lens_type = lens_type
73
+
167
74
  self.npool = npool
168
75
  self.z_min = z_min
169
76
  self.z_max = z_max
170
77
  self.cosmo = cosmology if cosmology else LambdaCDM(H0=70, Om0=0.3, Ode0=0.7)
78
+ self.directory = directory
171
79
 
172
- self.sampler_priors = dict(
173
- velocity_dispersion="velocity_dispersion_bernardi",
174
- axis_ratio="axis_ratio_rayleigh",
175
- )
176
- if sampler_priors:
177
- self.sampler_priors.update(sampler_priors)
178
- self.sampler_priors_params = dict(
179
- velocity_dispersion=dict(vd_min=0., vd_max=350.),
180
- axis_ratio=dict(q_min=0.2, q_max=1.),
181
- )
182
- if sampler_priors_params:
183
- self.sampler_priors_params.update(sampler_priors_params)
80
+ # decision dict fot creating interpolator
81
+ self.initialize_decision_dictionary(create_new_interpolator);
184
82
 
185
- optical_depth_function = optical_depth_function if optical_depth_function else "optical_depth_SIE_hemanta"
83
+ # default lens functions and samplers
84
+ self.lens_param_samplers, self.lens_param_samplers_params, self.lens_functions, self.lens_functions_params = self.default_lens_samplers_and_functions(lens_type)
186
85
 
187
- self.directory = directory
188
- self.c_n_i = dict(
189
- velocity_dispersion=dict(create_new=False, resolution=1000),
190
- axis_ratio=dict(create_new=False, resolution=1000),
191
- optical_depth=dict(create_new=False, resolution=200),
192
- z_to_Dc=dict(create_new=False, resolution=1000),
193
- Dc_to_z=dict(create_new=False, resolution=1000),
194
- angular_diameter_distance=dict(create_new=False, resolution=1000),
195
- differential_comoving_volume=dict(create_new=False, resolution=1000),
86
+ # change default lens functions and samplers if user input is given
87
+ # if lens functions and samplers are given but not parameters, then use default parameters
88
+ # it updates self.lens_param_samplers, self.lens_param_samplers_params, self.lens_functions, self.lens_functions_params
89
+ self.lens_functions_and_sampler_categorization(lens_param_samplers, lens_param_samplers_params, lens_functions, lens_functions_params);
90
+
91
+ # setting up astropy cosmology functions
92
+ self.create_lookup_table_fuction();
93
+
94
+ if self.lens_functions['cross_section'] == "interpolated_cross_section_function":
95
+ self.create_cross_section_function();
96
+ self.cross_section = getattr(self, "interpolated_cross_section_function")
97
+ else:
98
+ self.cross_section = getattr(self, self.lens_functions['cross_section'])
99
+ self.nbrs=None
100
+ self.values=None
101
+ self.cross_section_spline=None
102
+ self.sis_area_array=None
103
+
104
+ # lens sampler initialization
105
+ self.velocity_dispersion = self.lens_param_samplers['velocity_dispersion']
106
+ self.axis_ratio = self.lens_param_samplers['axis_ratio']
107
+ self.axis_rotation_angle = self.lens_param_samplers['axis_rotation_angle']
108
+ self.density_profile_slope = self.lens_param_samplers['density_profile_slope']
109
+ self.external_shear = self.lens_param_samplers['external_shear']
110
+ self.lens_redshift = self.lens_param_samplers['lens_redshift']
111
+ self.density_profile_slope_sl = self.lens_param_samplers['density_profile_slope_sl']
112
+ self.external_shear_sl = self.lens_param_samplers['external_shear_sl']
113
+
114
+ # lens function initialization
115
+ self.optical_depth = self.lens_functions['optical_depth']
116
+
117
+ ##################
118
+ # Initialization #
119
+ ##################
120
+ def default_lens_samplers_and_functions(self, lens_type):
121
+ """
122
+ Function to categorize the lens priors/samplers
123
+
124
+ Parameters
125
+ ----------
126
+ lens_type : `str`
127
+ lens type
128
+ e.g. 'epl_shear_galaxy' for elliptical power-law galaxy
129
+
130
+ Returns
131
+ -------
132
+ lens_priors_ : `dict`
133
+ dictionary of priors
134
+ lens_priors_params_ : `dict`
135
+ dictionary of priors parameters
136
+ lens_sampler_names_ : `dict`
137
+ dictionary of sampler names
138
+ lens_functions_ : `dict`
139
+ dictionary of lens functions
140
+ """
141
+
142
+ if lens_type == "epl_shear_galaxy":
143
+ lens_priors_ = dict(
144
+ source_redshift_sl="strongly_lensed_source_redshifts",
145
+ lens_redshift="lens_redshift_SDSS_catalogue_hemanta", #"lens_redshift_SDSS_catalogue_numerical",
146
+ velocity_dispersion="velocity_dispersion_ewoud",
147
+ axis_ratio="axis_ratio_rayleigh",
148
+ axis_rotation_angle="axis_rotation_angle_uniform",
149
+ external_shear="external_shear_normal",
150
+ density_profile_slope="density_profile_slope_normal",
151
+ external_shear_sl="external_shear_normal",
152
+ density_profile_slope_sl="density_profile_slope_normal",
153
+ )
154
+ lens_priors_params_ = dict(
155
+ source_redshift_sl=None,
156
+ lens_redshift=None,
157
+ velocity_dispersion=dict(sigma_min=100., sigma_max=400., alpha=0.94, beta=1.85, phistar=2.099e-2*(self.cosmo.h/0.7)**3, sigmastar=113.78),
158
+ axis_ratio=dict(q_min=0.2, q_max=1.),
159
+ axis_rotation_angle=dict(phi_min=0.0, phi_max=2 * np.pi),
160
+ external_shear=dict(mean=0., std=0.05),
161
+ density_profile_slope=dict(mean=1.99, std=0.149),
162
+ external_shear_sl=dict(mean=0., std=0.05, name="external_shear_sl"),
163
+ density_profile_slope_sl=dict(mean=2.078, std=0.16, name="density_profile_slope_sl"),
164
+ )
165
+ lens_functions_ = dict(
166
+ param_sampler_type="sample_all_routine_sie_sl",
167
+ strong_lensing_condition="rjs_with_cross_section_sie_feixu",
168
+ optical_depth="optical_depth_numerical",
169
+ cross_section="interpolated_cross_section_function",
170
+ )
171
+ lens_functions_params_ = dict(
172
+ strong_lensing_condition=None,
173
+ optical_depth=None,
174
+ param_sampler_type=dict(fast_sampler=True), # not implemented yet
175
+ cross_section=None,
176
+ )
177
+ elif lens_type == "sie_galaxy":
178
+ lens_priors_ = dict(
179
+ source_redshift_sl="strongly_lensed_source_redshifts",
180
+ lens_redshift="lens_redshift_SDSS_catalogue_hemanta", #"lens_redshift_SDSS_catalogue_numerical",
181
+ velocity_dispersion="velocity_dispersion_ewoud",
182
+ axis_ratio="axis_ratio_rayleigh",
183
+ axis_rotation_angle="axis_rotation_angle_uniform",
184
+ external_shear="external_shear_normal",
185
+ density_profile_slope="density_profile_slope_normal",
186
+ external_shear_sl="external_shear_normal",
187
+ density_profile_slope_sl="density_profile_slope_normal",
188
+ )
189
+ lens_priors_params_ = dict(
190
+ source_redshift_sl=None,
191
+ lens_redshift=None,
192
+ velocity_dispersion=dict(sigma_min=100., sigma_max=400., alpha=0.94, beta=1.85, phistar=2.099e-2*(self.cosmo.h/0.7)**3, sigmastar=113.78),
193
+ axis_ratio=dict(q_min=0.2, q_max=1.),
194
+ axis_rotation_angle=dict(phi_min=0.0, phi_max=2 * np.pi),
195
+ external_shear=dict(mean=0., std=0.),
196
+ density_profile_slope=dict(mean=2., std=0.),
197
+ external_shear_sl=dict(mean=0., std=0.),
198
+ density_profile_slope_sl=dict(mean=2., std=0.),
199
+ )
200
+ lens_functions_ = dict(
201
+ strong_lensing_condition="rjs_with_cross_section_sie_feixu",
202
+ optical_depth="optical_depth_numerical",
203
+ param_sampler_type="sample_all_routine_sie_sl",
204
+ cross_section="cross_section_sie_feixu",
205
+ )
206
+ lens_functions_params_ = dict(
207
+ strong_lensing_condition=None,
208
+ optical_depth=dict(interpolated_cross_section=True),
209
+ param_sampler_type=None,
210
+ cross_section=None,
211
+ )
212
+ elif lens_type == "sis_galaxy":
213
+ lens_priors_ = dict(
214
+ source_redshift_sl="strongly_lensed_source_redshifts",
215
+ lens_redshift="lens_redshift_SDSS_catalogue_sis",
216
+ velocity_dispersion="velocity_dispersion_choi",
217
+ axis_ratio="axis_ratio_uniform",
218
+ axis_rotation_angle="axis_rotation_angle_uniform",
219
+ external_shear="external_shear_normal",
220
+ density_profile_slope="density_profile_slope_normal",
221
+ external_shear_sl="external_shear_normal",
222
+ density_profile_slope_sl="density_profile_slope_normal",
223
+ )
224
+ lens_priors_params_ = dict(
225
+ source_redshift_sl=None,
226
+ lens_redshift=None,
227
+ velocity_dispersion=dict(sigma_min = 10., sigma_max = 420., alpha = 2.32, beta = 2.67, phistar = 8.0e-3*self.cosmo.h**3, sigmastar = 161.0),
228
+ axis_ratio=dict(q_min=1., q_max=1.),
229
+ axis_rotation_angle=dict(phi_min=0.0, phi_max=0.),
230
+ external_shear=dict(mean=0., std=0.),
231
+ density_profile_slope=dict(mean=2., std=0.),
232
+ external_shear_sl=dict(mean=0., std=0.),
233
+ density_profile_slope_sl=dict(mean=2., std=0.),
234
+ )
235
+ lens_functions_ = dict(
236
+ strong_lensing_condition="rjs_with_cross_section_sis",
237
+ optical_depth="optical_depth_sis_haris",
238
+ param_sampler_type="sample_all_routine_sis_sl",
239
+ cross_section="cross_section_sis",
240
+ )
241
+ lens_functions_params_ = dict(
242
+ strong_lensing_condition=None,
243
+ optical_depth=dict(interpolated_cross_section=True),
244
+ param_sampler_type=None,
245
+ cross_section=None,
246
+ )
247
+ else:
248
+ raise ValueError("lens_type not recognized")
249
+
250
+ return(lens_priors_, lens_priors_params_, lens_functions_, lens_functions_params_)
251
+
252
+ def initialize_decision_dictionary(self, create_new_interpolator):
253
+ """
254
+ Function to initialize decision dictionary for creating interpolator
255
+
256
+ Parameters
257
+ ----------
258
+ create_new_interpolator : `dict` or `bool`
259
+ dictionary to create new interpolator for velocity dispersion and optical depth.
260
+ """
261
+
262
+ # initialize the interpolator's parameters
263
+ self.create_new_interpolator = dict(
264
+ velocity_dispersion=dict(create_new=False, resolution=500),
265
+ axis_ratio=dict(create_new=False, resolution=500),
266
+ lens_redshift=dict(create_new=False, resolution=50),
267
+ optical_depth=dict(create_new=False, resolution=48),
268
+ comoving_distance=dict(create_new=False, resolution=500),
269
+ angular_diameter_distance=dict(create_new=False, resolution=500),
270
+ differential_comoving_volume=dict(create_new=False, resolution=500),
271
+ cross_section=dict(create_new=False, resolution=1000000),
272
+ density_profile_slope=dict(create_new=False, resolution=100),
273
+ lens_parameters_kde_sl=dict(create_new=False, resolution=5000),
196
274
  )
197
275
  if isinstance(create_new_interpolator, dict):
198
- self.c_n_i.update(create_new_interpolator)
276
+ self.create_new_interpolator.update(create_new_interpolator)
277
+ # if create_new_interpolator is True, create new interpolator for all
199
278
  elif create_new_interpolator is True:
200
- self.c_n_i = dict(
201
- velocity_dispersion=dict(create_new=True, resolution=1000),
202
- axis_ratio=dict(create_new=True, resolution=1000),
203
- optical_depth=dict(create_new=True, resolution=200),
204
- z_to_Dc=dict(create_new=True, resolution=1000),
205
- Dc_to_z=dict(create_new=True, resolution=1000),
206
- angular_diameter_distance=dict(create_new=True, resolution=1000),
207
- differential_comoving_volume=dict(create_new=True, resolution=1000),
208
- )
279
+ for key in self.create_new_interpolator.keys():
280
+ self.create_new_interpolator[key]['create_new'] = True
281
+
282
+ def lens_functions_and_sampler_categorization(self, lens_param_samplers, lens_param_samplers_params, lens_functions, lens_functions_params):
283
+ """
284
+ Function to initialize velocity dispersion sampler with it's settings. The reason I am seperating this from lens_priors_categorization is only a specific parameters needs special attention.
285
+
286
+ Parameters
287
+ ----------
288
+ lens_param_samplers : `str` or `function`
289
+ sampler name or function
290
+ lens_param_samplers_params : `dict`
291
+ sampler parameters
292
+ lens_functions : `str` or `function`
293
+ lens function name or function
294
+ lens_functions_params : `dict`
295
+ lens function parameters
296
+ """
297
+
298
+ # update the priors if input is given
299
+ if lens_param_samplers:
300
+ self.lens_param_samplers.update(lens_param_samplers)
301
+ if lens_param_samplers_params:
302
+ self.lens_param_samplers_params.update(lens_param_samplers_params)
303
+ if lens_functions:
304
+ self.lens_functions.update(lens_functions)
305
+ if lens_functions_params:
306
+ self.lens_functions_params.update(lens_functions_params)
307
+
308
+ sampler_prior_names = ['lens_redshift', 'velocity_dispersion', 'axis_ratio', 'axis_rotation_angle', 'external_shear', 'density_profile_slope', 'external_shear_sl', 'density_profile_slope_sl']
309
+
310
+ # if there is user input sampler prior params
311
+ # you can't only give lens_param_samplers_params, you have to give lens_param_samplers as well if you want to update the sampler priors
312
+ for name in sampler_prior_names: # e.g. name='axis_ratio'
313
+ # avoid None
314
+ if (lens_param_samplers is not None) and (name in lens_param_samplers):
315
+ # if there is user input function name, update the sampler priors
316
+ sampler_name = lens_param_samplers[name] # e.g. 'axis_ratio_rayleigh'
317
+ # check input sampler is string or function
318
+ if isinstance(sampler_name, str):
319
+ # available lens samplers for name e.g. 'axis_ratio'
320
+ dict_ = self.available_lens_prior_list_and_its_params[name] # e.g. {'axis_ratio_padilla_strauss': {'q_min': 0.2, 'q_max': 1.0}, ....}
321
+ if sampler_name in dict_: # e.g. 'axis_ratio_padilla_strauss'
322
+ param_dict = dict_[sampler_name] # e.g. {'q_min': 0.2, 'q_max': 1.0}
323
+ if (lens_param_samplers_params is None) or (lens_param_samplers_params[name] is None): # not a dictionary
324
+ self.lens_param_samplers_params[name] = param_dict
325
+ else: # if there is user input lens_param_samplers_params
326
+ param_dict.update(lens_param_samplers_params[name])
327
+ self.lens_param_samplers_params[name].update(param_dict) # user inputs will override the default values
328
+ else:
329
+ raise ValueError(f"{name} sampler {sampler_name} not available.\n Available {name} samplers and its parameters are: {dict_[name]}")
330
+ elif not callable(lens_param_samplers[name]):
331
+ raise ValueError(f"Given {name} sampler should be either a string name of available sampler or a function")
332
+
333
+
334
+ lens_function_names = ['optical_depth', 'cross_section']
335
+
336
+ # if there is user input function, update the sampler priors
337
+ for name in lens_function_names:
338
+ if (lens_functions is not None) and (name in lens_functions):
339
+ function_name = lens_functions[name]
340
+ if isinstance(function_name, str):
341
+ # available lens functions for name e.g. 'optical_depth'
342
+ dict_ = self.available_lens_functions_and_its_params[name]
343
+ if function_name in dict_:
344
+ param_dict = dict_[function_name]
345
+ if (lens_functions_params is None) or (lens_functions_params[name] is None): # not a dictionary
346
+ self.lens_functions_params[name] = param_dict
347
+ else: # if there is user input lens_functions_params
348
+ param_dict.update(lens_functions_params[name])
349
+ self.lens_functions_params[name].update(param_dict) # user inputs will override the default values
350
+ else:
351
+ raise ValueError(f"{name} function {function_name} not available.\n Available {name} functions and its parameters are: {dict_[name]}")
352
+ elif not callable(lens_functions[name]):
353
+ raise ValueError(f"Given {name} function should be either a string name of available function or a function")
354
+
355
+ # setting up multiprocessor function for lens redshift
356
+ if self.lens_type == 'epl_shear_galaxy':
357
+ if self.lens_param_samplers['velocity_dispersion'] == 'velocity_dispersion_ewoud':
358
+ print('using lens_redshift_epl_shear2_mp')
359
+ from .mp import lens_redshift_epl_shear2_mp
360
+ self._helper_sl_disribution_mp = lens_redshift_epl_shear2_mp
361
+ else:
362
+ print('using lens_redshift_epl_shear1_mp')
363
+ from .mp import lens_redshift_epl_shear1_mp
364
+ self._helper_sl_disribution_mp = lens_redshift_epl_shear1_mp
365
+ elif self.lens_type == 'sie_galaxy':
366
+ if self.lens_param_samplers['velocity_dispersion'] == 'velocity_dispersion_ewoud':
367
+ if self.lens_functions['cross_section']=='interpolated_cross_section_function':
368
+ print('using lens_redshift_sie4_mp')
369
+ from .mp import lens_redshift_sie4_mp
370
+ self._helper_sl_disribution_mp = lens_redshift_sie4_mp
371
+ else:
372
+ print('using lens_redshift_sie2_mp')
373
+ from .mp import lens_redshift_sie2_mp
374
+ self._helper_sl_disribution_mp = lens_redshift_sie2_mp
375
+ else:
376
+ if self.lens_functions['cross_section']=='interpolated_cross_section_function':
377
+ print('using lens_redshift_sie3_mp')
378
+ from .mp import lens_redshift_sie3_mp
379
+ self._helper_sl_disribution_mp = lens_redshift_sie3_mp
380
+ else:
381
+ print('using lens_redshift_sie1_mp')
382
+ from .mp import lens_redshift_sie1_mp
383
+ self._helper_sl_disribution_mp = lens_redshift_sie1_mp
384
+ elif self.lens_type == 'sis_galaxy':
385
+ if self.lens_param_samplers['velocity_dispersion'] == 'velocity_dispersion_ewoud':
386
+ print('using lens_redshift_sis2_mp')
387
+ from .mp import lens_redshift_sis2_mp
388
+ self._helper_sl_disribution_mp = lens_redshift_sis2_mp
389
+ else:
390
+ print('using lens_redshift_sis1_mp')
391
+ from .mp import lens_redshift_sis1_mp
392
+ self._helper_sl_disribution_mp = lens_redshift_sis1_mp
393
+
394
+
395
+ #####################
396
+ # Sampler functions #
397
+ #####################
398
+ def axis_ratio_rayleigh(self, size, sigma, get_attribute=False, **kwargs):
399
+ """
400
+ Function to sample axis ratio from rayleigh distribution with given velocity dispersion.
209
401
 
210
- vd_name = self.sampler_priors["velocity_dispersion"] # velocity dispersion sampler name
211
- tau_name = optical_depth_function # optical depth function name
402
+ Parameters
403
+ ----------
404
+ sigma : `float: array`
405
+ velocity dispersion of the lens galaxy
406
+ q_min, q_max : `float`
407
+ minimum and maximum axis ratio
408
+ get_attribute : `bool`
409
+ if True, returns a function that can be used to sample axis ratio
212
410
 
213
- self.create_lookup_table_fuction(self.z_max)
411
+ Returns
412
+ -------
413
+ q : `float: array`
414
+ axis ratio of the lens galaxy
214
415
 
215
- # setting velocity dispersion sampler method
216
- self.initialize_velocity_dispersion_sampler(vd_name);
416
+ Examples
417
+ --------
418
+ >>> from ler.lens_galaxy_population import OpticalDepth
419
+ >>> od = OpticalDepth(lens_param_samplers=dict(axis_ratio="axis_ratio_rayleigh"))
420
+ >>> print(od.axis_ratio(sigma=200.))
421
+ """
217
422
 
218
- # setting optical depth method
219
- if callable(optical_depth_function):
220
- self.strong_lensing_optical_depth = optical_depth_function
423
+ identifier_dict = {'name': "axis_ratio_rayleigh"}
424
+ identifier_dict['velocity_dispersion'] = self.lens_param_samplers_params['velocity_dispersion']
425
+ if identifier_dict['velocity_dispersion']: # if velocity_dispersion is not None
426
+ identifier_dict['velocity_dispersion']['name'] = str(self.lens_param_samplers['velocity_dispersion'])
427
+ identifier_dict['resolution'] = self.create_new_interpolator["axis_ratio"]["resolution"]
428
+ param_dict = self.available_lens_prior_list_and_its_params["axis_ratio"]["axis_ratio_rayleigh"]
429
+ if param_dict:
430
+ param_dict.update(kwargs)
221
431
  else:
222
- self.initialize_optical_depth_function(tau_name, vd_name);
432
+ param_dict = kwargs
433
+ identifier_dict.update(param_dict)
434
+
435
+ q_array = np.linspace(
436
+ identifier_dict["q_min"],
437
+ identifier_dict["q_max"],
438
+ identifier_dict["resolution"],
439
+ )
440
+ sigma_array = np.linspace(
441
+ identifier_dict['velocity_dispersion']["sigma_min"],
442
+ identifier_dict['velocity_dispersion']["sigma_max"],
443
+ 500,
444
+ )
445
+
446
+ q_pdf = lambda q, sigma: axis_ratio_rayleigh_pdf(
447
+ q=q,
448
+ sigma=sigma,
449
+ q_min=identifier_dict["q_min"],
450
+ q_max=identifier_dict["q_max"],
451
+ )
452
+
453
+ q_object = FunctionConditioning(
454
+ function=q_pdf,
455
+ x_array=q_array,
456
+ conditioned_y_array=sigma_array,
457
+ param_dict_given=identifier_dict,
458
+ directory=self.directory,
459
+ sub_directory="axis_ratio",
460
+ name="axis_ratio_rayleigh",
461
+ create_new=self.create_new_interpolator["axis_ratio"]["create_new"],
462
+ create_function_inverse=False,
463
+ create_function=True,
464
+ create_pdf=True,
465
+ create_rvs=True,
466
+ callback='rvs',
467
+ )
468
+
469
+ return q_object if get_attribute else q_object(size, sigma)
223
470
 
224
- def initialize_velocity_dispersion_sampler(self, vd_name):
471
+ def axis_ratio_padilla_strauss(self, size=1000, get_attribute=False, **kwargs):
225
472
  """
226
- Function to initialize velocity dispersion sampler
473
+ Function to sample axis ratio using Padilla and Strauss 2008 distribution for axis ratio
227
474
 
228
475
  Parameters
229
476
  ----------
230
- vd_name : `str`
231
- name of velocity dispersion sampler
477
+ size : `int`
478
+ sample size
479
+ q_min, q_max : `float`
480
+ minimum and maximum axis ratio
481
+ get_attribute : `bool`
482
+ if True, returns a function that can be used to sample axis ratio
483
+
484
+ Returns
485
+ -------
486
+ q : `float: array`
487
+ axis ratio of the lens galaxy
488
+
489
+ Examples
490
+ --------
491
+ >>> from ler.lens_galaxy_population import OpticalDepth
492
+ >>> od = OpticalDepth(lens_param_samplers=dict(axis_ratio="axis_ratio_padilla_strauss"))
493
+ >>> print(od.axis_ratio(size=10))
232
494
  """
233
495
 
234
- # setting up input parameters for interpolation
235
- # check vd_min and vd_max exists in sampler_priors_params
236
- try:
237
- self.vd_min = self.sampler_priors_params['velocity_dispersion']['vd_min']
238
- self.vd_max = self.sampler_priors_params['velocity_dispersion']['vd_max']
239
- except:
240
- self.vd_min = 10.
241
- self.vd_max = 350.
242
- self.sampler_priors_params['velocity_dispersion']=dict(vd_min=self.vd_min, vd_max=self.vd_max)
243
-
244
- # generating inverse cdf interpolator for velocity dispersion
245
- param_dict_given_ = dict(z_min=self.z_min, z_max=self.z_max, vd_min=self.vd_min, vd_max=self.vd_max, cosmology=self.cosmo, name=vd_name, resolution=self.c_n_i["velocity_dispersion"]["resolution"])
246
- sub_directory_ = vd_name
247
- x_ = np.linspace(self.vd_min, self.vd_max, self.c_n_i["velocity_dispersion"]["resolution"])
248
- category_ = "inv_cdf"
249
- create_new_ = self.c_n_i["velocity_dispersion"]["create_new"]
250
-
251
- if vd_name == "velocity_dispersion_gengamma":
252
- # setting up input parameters for interpolation
253
- pdf_func_ = lambda vd_: gengamma.pdf(vd_/161., a=2.32 / 2.67, c=2.67) # gengamma pdf
254
- conditioned_y_ = None
255
- dimension_ = 1
256
-
257
- elif vd_name == "velocity_dispersion_bernardi":
258
- # setting up input parameters for interpolation
259
- pdf_func_ = lambda vd_: vd_**4*phi_loc_bernardi(sigma=vd_, cosmology_h=self.cosmo.h)
260
- conditioned_y_ = None
261
- dimension_ = 1
262
-
263
- elif vd_name == "velocity_dispersion_ewoud":
264
- if self.z_min==0.0:
265
- self.zl_list = np.linspace(self.z_min+0.001, self.z_max, 100)
266
- else:
267
- self.zl_list = np.linspace(self.z_min, self.z_max, 100)
268
- # setting up input parameters for interpolation
269
- pdf_func_ = lambda vd_, zl_: phi(vd_, zl_, cosmology_h=self.cosmo.h)*self.differential_comoving_volume(np.array([zl_]))
270
- conditioned_y_ = self.zl_list
271
- dimension_ = 2
496
+ identifier_dict = {'name': "axis_ratio_padilla_strauss"}
497
+ identifier_dict['resolution'] = self.create_new_interpolator["axis_ratio"]["resolution"]
498
+ param_dict = self.available_lens_prior_list_and_its_params["axis_ratio"]["axis_ratio_padilla_strauss"]
499
+ if param_dict:
500
+ param_dict.update(kwargs)
501
+ else:
502
+ param_dict = kwargs
503
+ identifier_dict.update(param_dict)
504
+
505
+ # Using Padilla and Strauss 2008 distribution for axis ratio
506
+ q_array = np.array([0.04903276402927845, 0.09210526315789469, 0.13596491228070173, 0.20789473684210524, 0.2899703729522482, 0.3230132450331126, 0.35350877192982455, 0.37946148483792264, 0.4219298245614036, 0.4689525967235971, 0.5075026141512723, 0.5226472638550018, 0.5640350877192983, 0.6096491228070177, 0.6500000000000001, 0.6864848379226213, 0.7377192982456142, 0.7787295224817011, 0.8007581038689441, 0.822786685256187, 0.8668438480306729, 0.8973684210526317, 0.9254385964912283])
507
+ pdf = np.array([0.04185262687135349, 0.06114520695141845, 0.096997499638376, 0.1932510900336828, 0.39547914337673706, 0.49569751276216234, 0.6154609137685201, 0.7182049959882812, 0.920153741243567, 1.1573982157399754, 1.3353263628106684, 1.413149656448315, 1.5790713532948977, 1.7280185150744938, 1.8132994441344819, 1.8365803753840484, 1.8178662203211204, 1.748929843583365, 1.688182592496342, 1.6274353414093188, 1.4948487090314488, 1.402785526832393, 1.321844068356993])
508
+
509
+ q_object = FunctionConditioning(
510
+ function=pdf,
511
+ x_array=q_array,
512
+ conditioned_y_array=None,
513
+ param_dict_given=identifier_dict,
514
+ directory=self.directory,
515
+ sub_directory="axis_ratio",
516
+ name="axis_ratio_padilla_strauss",
517
+ create_new=self.create_new_interpolator["axis_ratio"]["create_new"],
518
+ create_function_inverse=False,
519
+ create_function=True,
520
+ create_pdf=True,
521
+ create_rvs=True,
522
+ callback='rvs',
523
+ )
272
524
 
525
+ return q_object if get_attribute else q_object(size)
526
+
527
+ def lens_redshift_SDSS_catalogue_numerical(self, size=1000, zs=None, get_attribute=False, **kwargs):
528
+ """
529
+ Function to sample lens redshifts, conditioned on the lens being strongly lensed
530
+
531
+ Parameters
532
+ ----------
533
+ size : `int`
534
+ sample size
535
+ zs : `float`
536
+ source redshifts
537
+ get_attribute : `bool`
538
+ if True, returns a function that can be used to sample lens redshifts
539
+
540
+ Returns
541
+ -------
542
+ zs : `float: array`
543
+ lens redshifts
544
+
545
+ Examples
546
+ --------
547
+ >>> from ler.lens_galaxy_population import OpticalDepth
548
+ >>> od = OpticalDepth(lens_param_samplers=dict(lens_redshift="lens_redshift_SDSS_catalogue_numerical"))
549
+ >>> print(od.lens_redshift(size=10, zs=1.0))
550
+ """
551
+
552
+ identifier_dict = {}
553
+ identifier_dict['name'] = "lens_redshift_numerical_"+self.lens_type
554
+ identifier_dict['resolution'] = self.create_new_interpolator["lens_redshift"]["resolution"]
555
+ identifier_dict['z_min'] = self.z_min
556
+ identifier_dict['z_max'] = self.z_max
557
+ identifier_dict['cosmology'] = self.cosmo
558
+ identifier_dict['velocity_dispersion'] = self.lens_param_samplers_params['velocity_dispersion']
559
+ if identifier_dict['velocity_dispersion']: # if velocity_dispersion is not None
560
+ identifier_dict['velocity_dispersion']['name'] = str(self.lens_param_samplers['velocity_dispersion'])
561
+ identifier_dict['axis_ratio'] = self.lens_param_samplers_params['axis_ratio']
562
+ if identifier_dict['axis_ratio']: # if axis_ratio is not None
563
+ identifier_dict['axis_ratio']['name'] = str(self.lens_param_samplers['axis_ratio'])
564
+ identifier_dict['axis_rotation_angle'] = self.lens_param_samplers_params['axis_rotation_angle']
565
+ if identifier_dict['axis_rotation_angle']: # if axis_rotation_angle is not None
566
+ identifier_dict['axis_rotation_angle']['name'] = str(self.lens_param_samplers['axis_rotation_angle'])
567
+ identifier_dict['density_profile_slope'] = self.lens_param_samplers_params['density_profile_slope']
568
+ if identifier_dict['density_profile_slope']: # if density_profile_slope is not None
569
+ identifier_dict['density_profile_slope']['name'] = str(self.lens_param_samplers['density_profile_slope'])
570
+ identifier_dict['external_shear'] = self.lens_param_samplers_params['external_shear']
571
+ if identifier_dict['external_shear']: # if external_shear is not None
572
+ identifier_dict['external_shear']['name'] = str(self.lens_param_samplers['external_shear'])
573
+
574
+ param_dict = self.available_lens_prior_list_and_its_params["lens_redshift"]["lens_redshift_SDSS_catalogue_numerical"]
575
+ if param_dict:
576
+ param_dict.update(kwargs)
273
577
  else:
274
- # this is to allow user to pass in their own sampler
275
- if callable(self.sampler_priors["velocity_dispersion"]):
276
- pass
277
- else:
278
- # raise error and terminate
279
- raise ValueError("velocity dispersion name not in `ler` library. And sampler_priors['velocity_dispersion'] is not a callable function.")
280
-
281
- self.vd_inv_cdf = interpolator_from_pickle(
282
- param_dict_given = param_dict_given_,
578
+ param_dict = kwargs
579
+ identifier_dict.update(param_dict)
580
+
581
+ print("Numerically solving the lens redshift distribution...")
582
+ zs_resolution = self.create_new_interpolator["optical_depth"]["resolution"]
583
+ zs_array = np.geomspace(self.z_min+0.001, self.z_max, zs_resolution) if self.z_min==0 else np.geomspace(self.z_min, self.z_max, zs_resolution)
584
+
585
+ zl_scaled2d = []
586
+ for i, zs_ in enumerate(zs_array):
587
+ buffer_ = np.linspace(0.0001, zs_-0.0001, identifier_dict['resolution'])
588
+ zl_scaled2d.append(buffer_/zs_)
589
+ zl_scaled2d = np.array(zl_scaled2d)
590
+
591
+ _, it_exist = interpolator_pickle_path(
592
+ param_dict_given=identifier_dict,
593
+ directory=self.directory,
594
+ sub_directory="lens_redshift",
595
+ interpolator_name=identifier_dict['name'],
596
+ )
597
+
598
+ create_new = self.create_new_interpolator["lens_redshift"]["create_new"]
599
+ if not it_exist or create_new:
600
+ number_density = self.lens_redshift_multiprocessing(zl_scaled2d, zs_array)
601
+ # number density is zero for zl=0 and infinite for zl=zs
602
+ # Adding zero at the first element of each row
603
+ zl_scaled2d = np.hstack((np.zeros((zl_scaled2d.shape[0], 1)), zl_scaled2d))
604
+ number_density = np.hstack((np.zeros((number_density.shape[0], 1)), number_density))
605
+ # Adding one at the last element of each row of zl_scaled2d
606
+ zl_scaled2d = np.hstack((zl_scaled2d, np.ones((zl_scaled2d.shape[0], 1))))
607
+ # Adding zero at the last element of each row of density
608
+ number_density = np.hstack((number_density, np.zeros((number_density.shape[0], 1))))
609
+ else:
610
+ number_density=None
611
+
612
+ zl_object = FunctionConditioning(
613
+ function=number_density,
614
+ x_array=zl_scaled2d,
615
+ conditioned_y_array=zs_array,
616
+ param_dict_given=identifier_dict,
617
+ directory=self.directory,
618
+ sub_directory="lens_redshift",
619
+ name=identifier_dict['name'],
620
+ create_new=create_new,
621
+ create_function_inverse=False,
622
+ create_function=True,
623
+ create_pdf=True,
624
+ create_rvs=True,
625
+ callback='rvs',
626
+ )
627
+
628
+ # un-scaled lens redshift
629
+ cdf_values = zl_object.cdf_values
630
+ x_array = zl_object.x_array
631
+ y_array = zl_object.conditioned_y_array
632
+ function_spline = zl_object.function_spline
633
+ pdf_norm_const = zl_object.pdf_norm_const
634
+
635
+ zl_object.function = njit(lambda x, y: cubic_spline_interpolator2d_array(x/y, y, function_spline, x_array, y_array))
636
+
637
+ zl_object.pdf = njit(lambda x, y: pdf_cubic_spline_interpolator2d_array(x/y, y, pdf_norm_const, function_spline, x_array, y_array))
638
+
639
+ zl_object.rvs = njit(lambda size, y: inverse_transform_sampler2d(size, y, cdf_values, x_array, y_array)*y)
640
+
641
+ return zl_object if get_attribute else zl_object(size, zs)
642
+
643
+ def lens_redshift_SDSS_catalogue_hemanta(self, size=1000, zs=None, get_attribute=False, **kwargs):
644
+ """
645
+ Function to sample lens redshifts, conditioned on the lens being strongly lensed
646
+
647
+ Parameters
648
+ ----------
649
+ size : `int`
650
+ sample size
651
+ zs : `float`
652
+ source redshifts
653
+ get_attribute : `bool`
654
+ if True, returns a function that can be used to sample lens redshifts
655
+
656
+ Returns
657
+ -------
658
+ zs : `float: array`
659
+ lens redshifts
660
+
661
+ Examples
662
+ --------
663
+ >>> from ler.lens_galaxy_population import OpticalDepth
664
+ >>> od = OpticalDepth(lens_param_samplers=dict(lens_redshift="lens_redshift_SDSS_catalogue_numerical"))
665
+ >>> print(od.lens_redshift(size=10, zs=1.0))
666
+ """
667
+
668
+ # default parameters
669
+ identifier_dict = {'name': 'lens_redshift_numerical_epl_shear_galaxy',
670
+ 'resolution': 50,
671
+ 'z_min': 0.0,
672
+ 'z_max': 10.0,
673
+ 'cosmology': LambdaCDM(H0=70, Om0=0.3, Ode0=0.7),
674
+ 'velocity_dispersion': {'sigma_min': 100.0,
675
+ 'sigma_max': 400.0,
676
+ 'alpha': 0.94,
677
+ 'beta': 1.85,
678
+ 'phistar': 0.02099,
679
+ 'sigmastar': 113.78,
680
+ 'name': 'velocity_dispersion_ewoud'},
681
+ 'axis_ratio': {'q_min': 0.2, 'q_max': 1.0, 'name': 'axis_ratio_rayleigh'},
682
+ 'axis_rotation_angle': {'phi_min': 0.0,
683
+ 'phi_max': 6.283185307179586,
684
+ 'name': 'axis_rotation_angle_uniform'},
685
+ 'density_profile_slope': {'mean': 1.99,
686
+ 'std': 0.149,
687
+ 'name': 'density_profile_slope_normal'},
688
+ 'external_shear': {'mean': 0.0, 'std': 0.05, 'name': 'external_shear_normal'}
689
+ }
690
+
691
+ print("Getting pre-computed lens redshift distribution...")
692
+ zs_resolution = 48
693
+ zs_array = np.geomspace(0.001, 10., zs_resolution)
694
+ zl_scaled2d = []
695
+ for i, zs_ in enumerate(zs_array):
696
+ buffer_ = np.linspace(0.0001, zs_-0.0001, identifier_dict['resolution'])
697
+ zl_scaled2d.append(buffer_/zs_)
698
+ zl_scaled2d = np.array(zl_scaled2d)
699
+
700
+ # get number_density
701
+ number_density = load_txt_from_module('ler', 'lens_galaxy_population.lens_param_data', 'number_density_zl_zs.txt')
702
+
703
+ # number density is zero for zl=0 and infinite for zl=zs
704
+ # Adding zero at the first element of each row
705
+ zl_scaled2d = np.hstack((np.zeros((zl_scaled2d.shape[0], 1)), zl_scaled2d))
706
+ # number_density = np.hstack((np.zeros((number_density.shape[0], 1)), number_density))
707
+ # Adding one at the last element of each row of zl_scaled2d
708
+ zl_scaled2d = np.hstack((zl_scaled2d, np.ones((zl_scaled2d.shape[0], 1))))
709
+ # Adding zero at the last element of each row of density
710
+ # number_density = np.hstack((number_density, np.zeros((number_density.shape[0], 1))))
711
+
712
+ zl_object = FunctionConditioning(
713
+ function=number_density,
714
+ x_array=zl_scaled2d,
715
+ conditioned_y_array=zs_array,
716
+ param_dict_given=identifier_dict,
283
717
  directory=self.directory,
284
- sub_directory=sub_directory_,
285
- name=vd_name,
286
- x = x_,
287
- pdf_func= pdf_func_,
288
- conditioned_y=conditioned_y_,
289
- dimension=dimension_,
290
- category=category_,
291
- create_new=create_new_,
718
+ sub_directory="lens_redshift",
719
+ name=identifier_dict['name'],
720
+ create_new=False,
721
+ create_function_inverse=False,
722
+ create_function=True,
723
+ create_pdf=True,
724
+ create_rvs=True,
725
+ callback='rvs',
292
726
  )
293
727
 
294
- self.sample_velocity_dispersion = self.sampler_priors["velocity_dispersion"]
728
+ # un-scaled lens redshift
729
+ cdf_values = zl_object.cdf_values
730
+ x_array = zl_object.x_array
731
+ y_array = zl_object.conditioned_y_array
732
+ function_spline = zl_object.function_spline
733
+ pdf_norm_const = zl_object.pdf_norm_const
734
+
735
+ zl_object.function = njit(lambda x, y: cubic_spline_interpolator2d_array(x/y, y, function_spline, x_array, y_array))
295
736
 
296
- def initialize_optical_depth_function(self, tau_name, vd_name):
737
+ zl_object.pdf = njit(lambda x, y: pdf_cubic_spline_interpolator2d_array(x/y, y, pdf_norm_const, function_spline, x_array, y_array))
738
+
739
+ zl_object.rvs = njit(lambda size, y: inverse_transform_sampler2d(size, y, cdf_values, x_array, y_array)*y)
740
+
741
+ return zl_object if get_attribute else zl_object(size, zs)
742
+
743
+ def intrinsic_lens_redshift(self, size=1000, get_attribute=False, **kwargs):
297
744
  """
298
- Function to initialize optical depth function.
745
+ Function to sample intrinsic lens redshifts, based on the intrinsic velocity dispersion of the lens galaxy.
299
746
 
300
747
  Parameters
301
748
  ----------
302
- tau_name : `str`
303
- name of optical depth function
304
- vd_name : `str`
305
- name of velocity dispersion sampler
749
+ size : `int`
750
+ sample size
751
+ zs : `float`
752
+ source redshifts
753
+ get_attribute : `bool`
754
+ if True, returns a function that can be used to sample lens redshifts
755
+
756
+ Returns
757
+ -------
758
+ zs : `float: array`
759
+ lens redshifts
760
+ """
761
+
762
+ identifier_dict = {'name': "intrinsic_lens_redshift"}
763
+ identifier_dict['z_min'] = self.z_min
764
+ identifier_dict['z_max'] = self.z_max
765
+ identifier_dict['cosmology'] = self.cosmo
766
+ identifier_dict['velocity_dispersion'] = self.lens_param_samplers_params['velocity_dispersion']
767
+ if identifier_dict['velocity_dispersion']: # if velocity_dispersion is not None
768
+ identifier_dict['velocity_dispersion']['name'] = str(self.lens_param_samplers['velocity_dispersion'])
769
+
770
+ identifier_dict['resolution'] = 500
771
+ # param_dict = self.available_lens_prior_list_and_its_params["lens_redshift"]["intrinsic_lens_redshift"]
772
+ # if param_dict:
773
+ # param_dict.update(kwargs)
774
+ # else:
775
+ # param_dict = kwargs
776
+ # identifier_dict.update(param_dict)
777
+
778
+ _, it_exist = interpolator_pickle_path(
779
+ param_dict_given=identifier_dict,
780
+ directory=self.directory,
781
+ sub_directory="lens_redshift",
782
+ interpolator_name=identifier_dict['name'],
783
+ )
784
+
785
+ create_new = self.create_new_interpolator["lens_redshift"]["create_new"]
786
+ if not it_exist or create_new:
787
+ z_min = 0.001 if self.z_min==0 else self.z_min
788
+ z_max = self.z_max
789
+ zl_array = np.linspace(z_min, z_max, identifier_dict['resolution'])
790
+ integrand = lambda sigma, z: self.velocity_dispersion.function(np.array([sigma]), np.array([z]))[0]*self.differential_comoving_volume.function(np.array([z]))[0]
791
+ # integral = [quad(integrand, 10., 420., args=(z))[0] for z in zl_array]
792
+ integral = [quad(integrand, identifier_dict['velocity_dispersion']['sigma_min'], identifier_dict['velocity_dispersion']['sigma_max'], args=(z))[0] for z in zl_array]
793
+ else:
794
+ zl_array = None
795
+ integral=None
796
+
797
+ zl_object = FunctionConditioning(
798
+ function=integral,
799
+ x_array=zl_array,
800
+ param_dict_given=identifier_dict,
801
+ directory=self.directory,
802
+ sub_directory="lens_redshift",
803
+ name=identifier_dict['name'],
804
+ create_new=create_new,
805
+ create_function_inverse=False,
806
+ create_function=True,
807
+ create_pdf=True,
808
+ create_rvs=True,
809
+ callback='rvs',
810
+ )
811
+
812
+ return zl_object if get_attribute else zl_object(size)
813
+
814
+ def lens_redshift_multiprocessing(self, zl_scaled2d, zs1d):
815
+ """
816
+ Compute the lens redshift distribution using multiprocessing.
817
+
818
+ Parameters
819
+ ----------
820
+ zl_scaled2d : array_like
821
+ 2D array of lens redshifts, scaled by the source redshift.
822
+ zs1d : array_like
823
+ 1D array of source redshifts.
824
+ zl_distribution_name : str
825
+ Name of the lens redshift distribution to compute.
826
+
827
+ Returns
828
+ -------
829
+ density_array : array_like
830
+ 2D array of the lens redshift distribution.
306
831
  """
832
+
833
+ sigma_args = self.lens_param_samplers_params["velocity_dispersion"]
834
+ sigma_args = [
835
+ sigma_args["sigma_min"],
836
+ sigma_args["sigma_max"],
837
+ sigma_args["alpha"],
838
+ sigma_args["beta"],
839
+ sigma_args["phistar"],
840
+ sigma_args["sigmastar"],
841
+ ]
842
+ # 3. q; q_rvs = self.axis_ratio.rvs
843
+ try:
844
+ q_args = [
845
+ np.array(self.axis_ratio.cdf_values),
846
+ np.array(self.axis_ratio.x_array),
847
+ np.array(self.axis_ratio.conditioned_y_array),
848
+ ]
849
+ except:
850
+ # SIS case
851
+ q_args = [
852
+ self.axis_ratio.info["q_min"],
853
+ self.axis_ratio.info["q_max"],
854
+ ]
855
+
856
+ # 4. Da; Da_function = self.angular_diameter_distance.function
857
+ Da_args = [
858
+ np.array(self.angular_diameter_distance.function_spline),
859
+ np.array(self.angular_diameter_distance.x_array),
860
+ ]
861
+
862
+ # 5. dVcdz; dVcdz_function = self.differential_comoving_volume.function
863
+ dVcdz_args = [
864
+ np.array(self.differential_comoving_volume.function_spline),
865
+ np.array(self.differential_comoving_volume.x_array),
866
+ ]
867
+
868
+ # 6. idx; index to order the results
869
+ idx = np.arange(len(zs1d))
870
+
871
+ # 7. cross section
872
+ # cs_fn = self.cross_section_function
873
+ cs_args = [
874
+ self.nbrs,
875
+ np.array(self.values),
876
+ np.array(self.cross_section_spline),
877
+ np.array(self.sis_area_array),
878
+ ]
879
+
880
+ # 8. phi; axis_rotation_angle = self.axis_rotation_angle.rvs
881
+ phi_args = self.lens_param_samplers_params["axis_rotation_angle"]
882
+ phi_args = [
883
+ phi_args["phi_min"],
884
+ phi_args["phi_max"],
885
+ ]
886
+
887
+ # 9. shear; external_shear = self.external_shear.rvs
888
+ shear_args = self.lens_param_samplers_params["external_shear"]
889
+ shear_args = [
890
+ shear_args['mean'],
891
+ shear_args['std'],
892
+ ]
893
+
894
+ # 10. slope; density_profile_slope = self.density_profile_slope.rvs
895
+ slope_args = self.lens_param_samplers_params["density_profile_slope"]
896
+ slope_args = [
897
+ slope_args['mean'],
898
+ slope_args['std'],
899
+ ]
900
+
901
+ input_params = np.array([(zs1d[i], zl_scaled2d[i], sigma_args, q_args, Da_args, dVcdz_args, idx[i], cs_args, phi_args, shear_args, slope_args) for i in range(len(zs1d))], dtype=object)
902
+
903
+ print("Computing lens redshift distribution with multiprocessing...")
904
+ # with tqdm
905
+ mp_fn = self._helper_sl_disribution_mp
906
+ density_array = np.zeros_like(zl_scaled2d)
907
+ with Pool(processes=self.npool) as pool:
908
+ for result in tqdm(
909
+ pool.imap_unordered(mp_fn, input_params),
910
+ total=len(zs1d),
911
+ ncols=100,
912
+ disable=False,
913
+ ):
914
+ # print(result)
915
+ (
916
+ iter_i,
917
+ density_,
918
+ ) = result
919
+
920
+ density_array[iter_i] = density_
921
+
922
+ # # without multiprocessing, just for loop with tqdm
923
+ # density_array = np.zeros_like(zl_scaled2d)
924
+ # for i in range(len(zs1d)):
925
+ # iter_i, density_ = mp_fn(input_params[i])
926
+ # density_array[iter_i] = density_
927
+ # except:
928
+ # return input_params
929
+
930
+ return density_array
931
+
932
+ def axis_rotation_angle_uniform(self, size, get_attribute=False, **kwargs):
933
+ """
934
+ Function to sample the axis rotation angle of the elliptical lens galaxy from a uniform distribution.
935
+
936
+ Parameters
937
+ ----------
938
+ size : `int`
939
+ number of lens parameters to sample
940
+ get_attribute : `bool`
941
+ if True, returns a function that can be called with size as input
307
942
 
308
- if tau_name=="optical_depth_SIS_haris":
309
- self.sample_axis_ratio = axis_ratio_SIS
310
- # no interpolation needed
311
- optical_depth_setter = getattr(self, tau_name)
943
+ Returns
944
+ -------
945
+ phi : `numpy.ndarray`
946
+ axis rotation angle of the elliptical lens galaxy
947
+
948
+ Examples
949
+ --------
312
950
 
313
- elif callable(tau_name):
314
- self.sample_axis_ratio = axis_ratio_SIS
315
- optical_depth_setter = tau_name
951
+ >>> from ler.lens_galaxy_population import OpticalDepth
952
+ >>> od = OpticalDepth(lens_param_samplers=dict(axis_rotation_angle="axis_rotation_angle_uniform"))
953
+ >>> print(od.axis_rotation_angle_uniform(size=10))
954
+ """
316
955
 
956
+ identifier_dict = {'name': "axis_rotation_angle_uniform"}
957
+ param_dict = self.available_lens_prior_list_and_its_params["axis_rotation_angle"]["axis_rotation_angle_uniform"]
958
+ if param_dict:
959
+ param_dict.update(kwargs)
317
960
  else:
318
- # setting up input parameters for interpolation
319
- resolution = self.c_n_i["optical_depth"]["resolution"]
320
- param_dict_given_ = dict(z_min=self.z_min, z_max=self.z_max, vd_min=self.vd_min, vd_max=self.vd_max, cosmology=self.cosmo, tau_name=tau_name, vd_name=vd_name, q_name=self.sampler_priors["axis_ratio"], resolution=resolution)
321
- #self.param_dict_given_ = param_dict_given_
322
- sub_directory_ = tau_name
323
- if self.z_min==0.0:
324
- x_ = np.geomspace(self.z_min+0.001, self.z_max, resolution)
325
- else:
326
- x_ = np.geomspace(self.z_min, self.z_max, resolution)
327
- pdf_func_ = self.optical_depth_multiprocessing
328
- dimension_ = 1
329
- category_ = "function"
330
- create_new_ = self.c_n_i["optical_depth"]["create_new"]
331
-
332
- if tau_name == "optical_depth_SIS_hemanta" or tau_name == "SIS":
333
- from .mp import optical_depth_sis_mp
334
- self.sample_axis_ratio = axis_ratio_SIS
335
- self.tau_mp_routine = optical_depth_sis_mp
336
-
337
- elif tau_name=="optical_depth_SIE_hemanta" or tau_name == "SIE":
338
- # axis-ratio sampler
339
- if self.sampler_priors["axis_ratio"]=="axis_ratio_rayleigh":
340
- self.sample_axis_ratio = axis_ratio_rayleigh
341
- else:
342
- self.sample_axis_ratio = self.sampler_priors["axis_ratio"]
961
+ param_dict = kwargs
962
+ identifier_dict.update(param_dict)
963
+
964
+ low = param_dict["phi_min"]
965
+ high = param_dict["phi_max"]
966
+ phi_rvs = njit(lambda size: np.random.uniform(
967
+ low=low,
968
+ high=high,
969
+ size=size,
970
+ ))
971
+ if param_dict["phi_max"] == param_dict["phi_min"]:
972
+ phi_pdf = lambda phi: 1. if phi == param_dict["phi_min"] else 0.
973
+ else:
974
+ phi_pdf = lambda phi: 1/(param_dict["phi_max"]-param_dict["phi_min"])
975
+
976
+ phi_object =FunctionConditioning(
977
+ function=None,
978
+ x_array=None,
979
+ param_dict_given=param_dict,
980
+ create_rvs=phi_rvs,
981
+ create_pdf=phi_pdf,
982
+ callback='rvs',
983
+ )
343
984
 
344
- if vd_name == "velocity_dispersion_ewoud":
345
- from .mp import optical_depth_sie2_mp
346
- self.tau_mp_routine = optical_depth_sie2_mp
347
- else:
348
- from .mp import optical_depth_sie1_mp
349
- self.tau_mp_routine = optical_depth_sie1_mp
350
-
351
- # this will initialize the interpolator
352
- optical_depth_setter = interpolator_from_pickle(
353
- param_dict_given = param_dict_given_,
354
- directory=self.directory,
355
- sub_directory=sub_directory_,
356
- name=tau_name,
357
- x = x_,
358
- pdf_func= pdf_func_,
359
- conditioned_y=None,
360
- dimension=dimension_,
361
- category=category_,
362
- create_new=create_new_,
363
- )
985
+ return phi_object if get_attribute else phi_object.rvs(size)
986
+
987
+ def axis_ratio_uniform(self, size, get_attribute=False, **kwargs):
988
+ """
989
+ Function to sample the axis ratio of the elliptical lens galaxy from a uniform distribution.
990
+
991
+ Parameters
992
+ ----------
993
+ size : `int`
994
+ number of lens parameters to sample
995
+ get_attribute : `bool`
996
+ if True, returns a function that can be called with size as input
364
997
 
365
- #self.optical_depth_setter = optical_depth_setter
366
- self.strong_lensing_optical_depth = optical_depth_setter
367
- self.sample_axis_ratio = self.sampler_priors["axis_ratio"]
998
+ Returns
999
+ -------
1000
+ q : `numpy.ndarray`
1001
+ axis ratio of the elliptical lens galaxy
1002
+
1003
+ Examples
1004
+ --------
368
1005
 
369
- def axis_ratio_rayleigh(self, sigma, q_min=0.2, q_max=1.0, get_attribute=False, param=None, **kwargs):
1006
+ >>> from ler.lens_galaxy_population import OpticalDepth
1007
+ >>> od = OpticalDepth(lens_param_samplers=dict(axis_ratio="axis_ratio_uniform"))
1008
+ >>> print(od.axis_ratio_uniform(size=10))
370
1009
  """
371
- Function to sample axis ratio from rayleigh distribution with given velocity dispersion.
1010
+
1011
+ identifier_dict = {'name': "axis_ratio_uniform"}
1012
+ param_dict = self.available_lens_prior_list_and_its_params["axis_ratio"]["axis_ratio_uniform"]
1013
+ if param_dict:
1014
+ param_dict.update(kwargs)
1015
+ else:
1016
+ param_dict = kwargs
1017
+ identifier_dict.update(param_dict)
1018
+
1019
+ low = param_dict["q_min"]
1020
+ high = param_dict["q_max"]
1021
+ q_rvs = njit(lambda size: np.random.uniform(
1022
+ low=low,
1023
+ high=high,
1024
+ size=size,
1025
+ ))
1026
+ if param_dict["q_max"] == param_dict["q_min"]:
1027
+ q_pdf = lambda q: 1. if q == param_dict["q_min"] else 0.
1028
+ else:
1029
+ q_pdf = lambda q: 1/(param_dict["q_max"]-param_dict["q_min"])
1030
+
1031
+ q_object =FunctionConditioning(
1032
+ function=None,
1033
+ x_array=None,
1034
+ param_dict_given=param_dict,
1035
+ create_rvs=q_rvs,
1036
+ create_pdf=q_pdf,
1037
+ callback='rvs',
1038
+ )
1039
+
1040
+ return q_object if get_attribute else q_object.rvs(size)
1041
+
1042
+ def external_shear_normal(self, size, get_attribute=False, **kwargs):
1043
+ """
1044
+ Function to sample the external shear parameters from a normal distribution.
372
1045
 
373
1046
  Parameters
374
1047
  ----------
375
- sigma : `float: array`
376
- velocity dispersion of the lens galaxy
377
- q_min, q_max : `float`
378
- minimum and maximum axis ratio
1048
+ size : `int`
1049
+ number of lens parameters to sample
379
1050
  get_attribute : `bool`
380
- if True, returns a function that can be used to sample axis ratio
1051
+ if True, returns a function that can be called with size as input
381
1052
 
382
1053
  Returns
383
1054
  -------
384
- q : `float: array`
385
- axis ratio of the lens galaxy
1055
+ gamma_1 : `numpy.ndarray`
1056
+ shear component in the x-direction
1057
+ gamma_2 : `numpy.ndarray`
1058
+ shear component in the y-direction
386
1059
 
387
1060
  Examples
388
1061
  --------
1062
+
389
1063
  >>> from ler.lens_galaxy_population import OpticalDepth
390
- >>> od = OpticalDepth(sampler_priors=dict(axis_ratio="axis_ratio_rayleigh"))
391
- >>> print(od.sample_axis_ratio(sigma=200.))
1064
+ >>> od = OpticalDepth(lens_param_samplers=dict(external_shear="external_shear_normal"))
1065
+ >>> print(od.external_shear_normal(size=10))
392
1066
  """
393
1067
 
1068
+ identifier_dict = {'name': "external_shear_normal"}
1069
+ param = self.available_lens_prior_list_and_its_params["external_shear"]["external_shear_normal"]
394
1070
  if param:
395
- q_min = param["q_min"]
396
- q_max = param["q_max"]
397
-
1071
+ param.update(kwargs)
1072
+ else:
1073
+ param = kwargs
1074
+ identifier_dict.update(param)
1075
+
1076
+ mean = param["mean"]
1077
+ std = param["std"]
1078
+ shear_rvs = njit(lambda size: np.random.normal(
1079
+ loc=mean,
1080
+ scale=std,
1081
+ size=(2,size),
1082
+ ))
1083
+ shear_pdf = njit(lambda shear1, shear2: normal_pdf_2d(
1084
+ x=shear1,
1085
+ y=shear2,
1086
+ mean_x=mean,
1087
+ mean_y=mean,
1088
+ std_x=std,
1089
+ std_y=std,
1090
+ ))
1091
+
1092
+ shear_object =FunctionConditioning(
1093
+ function=None,
1094
+ x_array=None,
1095
+ param_dict_given=identifier_dict,
1096
+ create_rvs=shear_rvs,
1097
+ create_pdf=shear_pdf,
1098
+ callback='rvs',
1099
+ )
1100
+
1101
+ return shear_object if get_attribute else shear_object.rvs(size)
1102
+
1103
+ def external_shear_numerical_hemanta(self, size, get_attribute=False, **kwargs):
1104
+ """
1105
+ Function to sample the external shear parameters from a normal distribution.
1106
+
1107
+ Parameters
1108
+ ----------
1109
+ size : `int`
1110
+ number of lens parameters to sample
1111
+ get_attribute : `bool`
1112
+ if True, returns a function that can be called with size as input
1113
+
1114
+ Returns
1115
+ -------
1116
+ gamma_1 : `numpy.ndarray`
1117
+ shear component in the x-direction
1118
+ gamma_2 : `numpy.ndarray`
1119
+ shear component in the y-direction
1120
+
1121
+ Examples
1122
+ --------
1123
+
1124
+ >>> from ler.lens_galaxy_population import OpticalDepth
1125
+ >>> od = OpticalDepth(lens_param_samplers=dict(external_shear="external_shear_normal"))
1126
+ >>> print(od.external_shear_normal(size=10))
1127
+ """
1128
+
1129
+ identifier_dict = {
1130
+ 'name': 'external_shear_numerical_hemanta',
1131
+ 'external_shear_normal': {'mean': 0., 'std': 0.05},
1132
+ }
1133
+
1134
+ gamma1, gamma2 = load_txt_from_module('ler', 'lens_galaxy_population.lens_param_data', 'external_shear_sl.txt')
1135
+
1136
+ shear_object =FunctionConditioning(
1137
+ x_array=gamma1,
1138
+ y_array=gamma2,
1139
+ gaussian_kde=True,
1140
+ param_dict_given=identifier_dict,
1141
+ create_rvs=True,
1142
+ create_pdf=True,
1143
+ callback='rvs',
1144
+ )
1145
+
1146
+ return shear_object if get_attribute else shear_object.rvs(size)
1147
+
1148
+ def density_profile_slope_normal(self, size, get_attribute=False, **kwargs):
1149
+ """
1150
+ Function to sample the lens galaxy density profile slope with normal distribution.
1151
+
1152
+ Parameters
1153
+ ----------
1154
+ size : `int`
1155
+ number of lens parameters to sample
1156
+ get_attribute : `bool`
1157
+ if True, returns a function that can be used to sample velocity dispersion
1158
+ **kwargs : `dict`
1159
+ additional parameters to be passed to the function,
1160
+ e.g. `mean` and `std` for the normal distribution
1161
+
1162
+ Returns
1163
+ -------
1164
+ slope : `float`
1165
+ density profile slope of the lens galaxy
1166
+
1167
+ Examples
1168
+ --------
1169
+ >>> from ler.lens_galaxy_population import OpticalDepth
1170
+ >>> od = OpticalDepth(lens_param_samplers=dict(density_profile_slope="density_profile_slope_normal"))
1171
+ >>> print(od.density_profile_slope_normal(size=10))
1172
+ """
1173
+
1174
+ identifier_dict = {'name': "density_profile_slope_normal"}
1175
+
1176
+ param = self.available_lens_prior_list_and_its_params["density_profile_slope"]["density_profile_slope_normal"]
1177
+ if param:
1178
+ param.update(kwargs)
1179
+ else:
1180
+ param = kwargs
1181
+ identifier_dict.update(param)
1182
+
1183
+ mean = param["mean"]
1184
+ std = param["std"]
1185
+ slope_rvs = njit(lambda size: np.random.normal(
1186
+ loc=mean,
1187
+ scale=std,
1188
+ size=size,
1189
+ ))
1190
+ slope_pdf = njit(lambda slope: normal_pdf(
1191
+ x=slope,
1192
+ mean=mean,
1193
+ std=std,
1194
+ ))
1195
+
1196
+ slope_object =FunctionConditioning(
1197
+ function=None,
1198
+ x_array=None,
1199
+ param_dict_given=identifier_dict,
1200
+ create_rvs=slope_rvs,
1201
+ create_pdf=slope_pdf,
1202
+ callback='rvs',
1203
+ )
1204
+
398
1205
  if get_attribute:
399
- return njit(lambda sigma: axis_ratio_rayleigh(sigma, q_min, q_max))
1206
+ return slope_object
400
1207
  else:
401
- return axis_ratio_rayleigh(sigma, q_min, q_max)
1208
+ return slope_rvs(size)
402
1209
 
403
- def axis_ratio_padilla_strauss(self, size=1000, q_min=0.2, q_max=1.0, get_attribute=False, param=None, **kwargs):
1210
+ def density_profile_slope_numerical_hemanta(self, size, get_attribute=False, **kwargs):
1211
+ """
1212
+ Function to sample the lens galaxy density profile slope with normal distribution.
1213
+
1214
+ Parameters
1215
+ ----------
1216
+ size : `int`
1217
+ number of lens parameters to sample
1218
+ get_attribute : `bool`
1219
+ if True, returns a function that can be used to sample velocity dispersion
1220
+ **kwargs : `dict`
1221
+ additional parameters to be passed to the function,
1222
+ e.g. `mean` and `std` for the normal distribution
1223
+
1224
+ Returns
1225
+ -------
1226
+ slope : `float`
1227
+ density profile slope of the lens galaxy
1228
+
1229
+ Examples
1230
+ --------
1231
+ >>> from ler.lens_galaxy_population import OpticalDepth
1232
+ >>> od = OpticalDepth(lens_param_samplers=dict(density_profile_slope="density_profile_slope_normal"))
1233
+ >>> print(od.density_profile_slope_normal(size=10))
1234
+ """
1235
+
1236
+ identifier_dict = {
1237
+ 'name': 'density_profile_slope_numerical_hemanta',
1238
+ 'density_profile_slope_normal': {'mean': 1.99, 'std': 0.149},
1239
+ }
1240
+
1241
+ gamma = load_txt_from_module('ler', 'lens_galaxy_population.lens_param_data', 'density_profile_slope_sl.txt')
1242
+
1243
+ slope_object =FunctionConditioning(
1244
+ x_array=gamma,
1245
+ gaussian_kde=True,
1246
+ param_dict_given=identifier_dict,
1247
+ create_rvs=True,
1248
+ create_pdf=True,
1249
+ callback='rvs',
1250
+ )
1251
+
1252
+ return slope_object if get_attribute else slope_object.rvs(size)
1253
+
1254
+ def lens_redshift_SDSS_catalogue_sis(self, size, zs, get_attribute=False, **kwargs):
404
1255
  """
405
- Function to sample axis ratio using Padilla and Strauss 2008 distribution for axis ratio
1256
+ Function to sample lens redshifts, conditioned on the lens being strongly lensed
406
1257
 
407
1258
  Parameters
408
1259
  ----------
409
- size : `int`
410
- sample size
411
- q_min, q_max : `float`
412
- minimum and maximum axis ratio
1260
+ zs : `float`
1261
+ source redshifts
413
1262
  get_attribute : `bool`
414
- if True, returns a function that can be used to sample axis ratio
1263
+ If True, returns a function that can be called with zs as input
415
1264
 
416
1265
  Returns
417
1266
  -------
418
- q : `float: array`
419
- axis ratio of the lens galaxy
1267
+ zl : `float`
1268
+ lens redshifts
420
1269
 
421
1270
  Examples
422
1271
  --------
423
- >>> from ler.lens_galaxy_population import OpticalDepth
424
- >>> od = OpticalDepth(sampler_priors=dict(axis_ratio="axis_ratio_padilla_strauss"))
425
- >>> print(od.sample_axis_ratio(size=10))
1272
+ >>> from ler.lens_galaxy_population import LensGalaxyParameterDistribution
1273
+ >>> lens = LensGalaxyParameterDistribution()
1274
+ >>> lens.lens_redshift_SDSS_catalogue_sis(zs=1.0)
426
1275
  """
427
1276
 
428
- try:
429
- size = len(kwargs["sigma"])
430
- except:
431
- pass
432
- if param:
433
- q_min = param["q_min"]
434
- q_max = param["q_max"]
1277
+ # # Old way
1278
+ # splineDc = self.comoving_distance.function_spline # spline coefficients for the comoving distance and redshifts
1279
+ # splineDcInv = self.comoving_distance.function_inverse_spline # spline coefficients for the redshifts and comoving distance
1280
+ # u = np.linspace(0, 1, 500)
1281
+ # cdf = (10 * u**3 - 15 * u**4 + 6 * u**5) # See the integral of Eq. A7 of https://arxiv.org/pdf/1807.07062.pdf (cdf)
1282
+ # zs = np.array([zs]).reshape(-1)
1283
+
1284
+ # New way
1285
+ identifier_dict = {'name': "lens_redshift_SDSS_catalogue_sis"}
1286
+ identifier_dict['z_min'] = self.z_min
1287
+ identifier_dict['z_max'] = self.z_max
1288
+ identifier_dict['cosmology'] = LambdaCDM(H0=70, Om0=0.3, Ode0=0.7)
1289
+ identifier_dict['velocity_dispersion'] = dict(sigma_min = 10., sigma_max = 420., alpha = 2.32, beta = 2.67, phistar = 8.0e-3*self.cosmo.h**3, sigmastar = 161.0)
1290
+ identifier_dict['resolution'] = self.create_new_interpolator["lens_redshift"]["resolution"]
1291
+ param_dict = self.available_lens_prior_list_and_its_params["lens_redshift"]["lens_redshift_SDSS_catalogue_sis"]
1292
+ if param_dict:
1293
+ param_dict.update(kwargs)
1294
+ else:
1295
+ param_dict = kwargs
1296
+ identifier_dict.update(param_dict)
435
1297
 
436
- # Using Padilla and Strauss 2008 distribution for axis ratio
437
- q = np.array([0.04903276402927845, 0.09210526315789469, 0.13596491228070173, 0.20789473684210524, 0.2899703729522482, 0.3230132450331126, 0.35350877192982455, 0.37946148483792264, 0.4219298245614036, 0.4689525967235971, 0.5075026141512723, 0.5226472638550018, 0.5640350877192983, 0.6096491228070177, 0.6500000000000001, 0.6864848379226213, 0.7377192982456142, 0.7787295224817011, 0.8007581038689441, 0.822786685256187, 0.8668438480306729, 0.8973684210526317, 0.9254385964912283])
438
- pdf = np.array([0.04185262687135349, 0.06114520695141845, 0.096997499638376, 0.1932510900336828, 0.39547914337673706, 0.49569751276216234, 0.6154609137685201, 0.7182049959882812, 0.920153741243567, 1.1573982157399754, 1.3353263628106684, 1.413149656448315, 1.5790713532948977, 1.7280185150744938, 1.8132994441344819, 1.8365803753840484, 1.8178662203211204, 1.748929843583365, 1.688182592496342, 1.6274353414093188, 1.4948487090314488, 1.402785526832393, 1.321844068356993])
1298
+ zl_resolution = identifier_dict['resolution']
1299
+ x_array = np.linspace(0., 1., zl_resolution)
1300
+ pdf_ = lambda x: 30* x**2 * (1-x)**2 # Haris et al. 2018 (A7)
439
1301
 
440
- spline_coeff = interpolator_from_pickle(
441
- param_dict_given = dict(parameter="axis_ratio", pdf="Padilla and Strauss 2008 distribution for axis ratio"),
442
- directory=self.directory,
443
- sub_directory="axis_ratio",
444
- name="axis_ratio_spline_coeff",
445
- x = q,
446
- pdf_func=None,
447
- y=pdf,
448
- conditioned_y=None,
449
- dimension=1,
450
- category="function",
451
- create_new=True,
452
- )
453
-
454
- resolution = self.c_n_i["axis_ratio"]["resolution"]
455
- create_new = self.c_n_i["axis_ratio"]["create_new"]
456
- q_array = np.linspace(q_min, q_max, resolution)
457
- pdf_array = cubic_spline_interpolator(q_array, spline_coeff[0], spline_coeff[1])
458
-
459
- q_inv_cdf = interpolator_from_pickle(
460
- param_dict_given = dict(parameter="axis_ratio", pdf="Padilla and Strauss 2008 distribution for axis ratio", q_min=q_min, q_max=q_max, resolution=resolution),
1302
+ zl_object = FunctionConditioning(
1303
+ function=pdf_,
1304
+ x_array=x_array,
1305
+ param_dict_given=identifier_dict,
461
1306
  directory=self.directory,
462
- sub_directory="axis_ratio",
463
- name="axis_ratio",
464
- x = q_array,
465
- pdf_func=None,
466
- y=pdf_array,
467
- conditioned_y=None,
468
- dimension=1,
469
- category="inv_cdf",
470
- create_new=create_new,
1307
+ sub_directory="lens_redshift",
1308
+ name=identifier_dict['name'],
1309
+ create_new=self.create_new_interpolator["lens_redshift"]["create_new"],
1310
+ create_function_inverse=False,
1311
+ create_function=True,
1312
+ create_pdf=True,
1313
+ create_rvs=True,
1314
+ callback='rvs',
471
1315
  )
472
1316
 
473
- # def sampler(sigma):
474
- # size = len(sigma)
475
- # return inverse_transform_sampler(size, q_inv_cdf[0], q_inv_cdf[1])
476
-
477
- if get_attribute:
478
- return njit(lambda sigma: inverse_transform_sampler(len(sigma), q_inv_cdf[0], q_inv_cdf[1]))
479
- else:
480
- return inverse_transform_sampler(size, q_inv_cdf[0], q_inv_cdf[1])
1317
+ cdf_values_zl = zl_object.cdf_values
1318
+ function_spline_zl = zl_object.function_spline
1319
+ pdf_norm_const = zl_object.pdf_norm_const
1320
+ x_array_zl = zl_object.x_array
1321
+
1322
+ spline_Dc = self.comoving_distance.function_spline
1323
+ x_array_Dc = self.comoving_distance.x_array
1324
+ inverse_spline_Dc = self.comoving_distance.function_inverse_spline
1325
+ z_array_Dc = self.comoving_distance.z_array
1326
+
1327
+ @njit
1328
+ def zl_rvs(size, zs):
1329
+ r = inverse_transform_sampler(size, cdf_values_zl, x_array_zl)
1330
+ zs_Dc = cubic_spline_interpolator(zs, spline_Dc, x_array_Dc)
1331
+ zl_Dc = zs_Dc * r
1332
+ return cubic_spline_interpolator(zl_Dc, inverse_spline_Dc, z_array_Dc)
1333
+
1334
+ @njit
1335
+ def zl_pdf(zl, zs):
1336
+ r = zl / zs
1337
+ return cubic_spline_interpolator(r, function_spline_zl, x_array_zl)/pdf_norm_const
1338
+
1339
+ @njit
1340
+ def zl_function(zl, zs):
1341
+ r = zl / zs
1342
+ return cubic_spline_interpolator(r, function_spline_zl, x_array_zl)
1343
+
1344
+ zl_object.function = zl_function
1345
+ zl_object.pdf = zl_pdf
1346
+ zl_object.rvs = zl_rvs
481
1347
 
482
- def velocity_dispersion_gengamma(self, size, a=2.32 / 2.67, c=2.67, get_attribute=False, param=None, **kwargs):
1348
+ return zl_object if get_attribute else zl_object.rvs(size, zs)
1349
+
1350
+
1351
+ def velocity_dispersion_gengamma(self, size, get_attribute=False, **kwargs):
483
1352
  """
484
1353
  Function to sample velocity dispersion from gengamma distribution
485
1354
 
@@ -504,20 +1373,50 @@ class OpticalDepth():
504
1373
  Examples
505
1374
  --------
506
1375
  >>> from ler.lens_galaxy_population import OpticalDepth
507
- >>> od = OpticalDepth(sampler_priors=dict(velocity_dispersion="velocity_dispersion_gengamma"), sampler_priors_params=dict(velocity_dispersion=dict(a=2.32 / 2.67, c=2.67)))
508
- >>> print(od.sample_velocity_dispersion(size=10))
509
-
1376
+ >>> od = OpticalDepth(lens_param_samplers=dict(velocity_dispersion="velocity_dispersion_gengamma"), lens_param_samplers_params=dict(velocity_dispersion=dict(a=2.32 / 2.67, c=2.67)))
1377
+ >>> print(od.velocity_dispersion(size=10))
510
1378
  """
511
1379
 
512
- if param:
513
- a = param["a"]
514
- c = param["c"]
515
-
516
- if get_attribute:
517
- return lambda size: 161.*gengamma.rvs(a, c, size=size)
1380
+ identifier_dict = {'name': "velocity_dispersion_gengamma"}
1381
+ identifier_dict['z_min'] = self.z_min
1382
+ identifier_dict['z_max'] = self.z_max
1383
+ identifier_dict['cosmology'] = self.cosmo
1384
+ identifier_dict['resolution'] = self.create_new_interpolator["velocity_dispersion"]["resolution"]
1385
+ param_dict = self.available_lens_prior_list_and_its_params["velocity_dispersion"]["velocity_dispersion_gengamma"]
1386
+ if param_dict:
1387
+ param_dict.update(kwargs)
518
1388
  else:
519
- # sample velocity dispersion from gengamma distribution
520
- return 161.*gengamma.rvs(a, c, size=size) # km/s
1389
+ param_dict = kwargs
1390
+ identifier_dict.update(param_dict)
1391
+
1392
+ # setting up inputs for the interpolator
1393
+ sigma_array = np.linspace(
1394
+ identifier_dict['sigma_min'],
1395
+ identifier_dict['sigma_max'],
1396
+ identifier_dict["resolution"]
1397
+ )
1398
+ pdf_func_ = lambda sigma_: gengamma.pdf(
1399
+ sigma_/identifier_dict['sigmastar'],
1400
+ a= identifier_dict['alpha']/identifier_dict['beta'],
1401
+ c= identifier_dict['beta'],
1402
+ ) # gengamma pdf
1403
+
1404
+ sigma_object = FunctionConditioning(
1405
+ function=pdf_func_,
1406
+ x_array=sigma_array,
1407
+ param_dict_given=identifier_dict,
1408
+ directory=self.directory,
1409
+ sub_directory="velocity_dispersion",
1410
+ name=identifier_dict['name'],
1411
+ create_new=self.create_new_interpolator["velocity_dispersion"]['create_new'],
1412
+ create_function_inverse=False,
1413
+ create_function=True,
1414
+ create_pdf=True,
1415
+ create_rvs=True,
1416
+ callback='rvs',
1417
+ )
1418
+
1419
+ return sigma_object if get_attribute else sigma_object.rvs(size)
521
1420
 
522
1421
  def velocity_dispersion_bernardi(self, size, get_attribute=False, **kwargs):
523
1422
  """
@@ -538,16 +1437,53 @@ class OpticalDepth():
538
1437
  Examples
539
1438
  --------
540
1439
  >>> from ler.lens_galaxy_population import OpticalDepth
541
- >>> od = OpticalDepth(sampler_priors=dict(velocity_dispersion="velocity_dispersion_bernardi"))
542
- >>> print(od.sample_velocity_dispersion(size=10))
1440
+ >>> od = OpticalDepth(lens_param_samplers=dict(velocity_dispersion="velocity_dispersion_bernardi"))
1441
+ >>> print(od.velocity_dispersion(size=10))
543
1442
  """
544
1443
 
545
- vd_inv_cdf = self.vd_inv_cdf.copy()
546
- # get the interpolator (inverse cdf) and sample
547
- if get_attribute:
548
- return njit(lambda size: inverse_transform_sampler(size, vd_inv_cdf[0], vd_inv_cdf[1]))
1444
+ identifier_dict = {'name': "velocity_dispersion_bernardi"}
1445
+ identifier_dict['z_min'] = self.z_min
1446
+ identifier_dict['z_max'] = self.z_max
1447
+ identifier_dict['cosmology'] = self.cosmo
1448
+ identifier_dict['name'] = "velocity_dispersion_bernardi"
1449
+ identifier_dict['resolution'] = self.create_new_interpolator["velocity_dispersion"]["resolution"]
1450
+ param_dict = self.available_lens_prior_list_and_its_params["velocity_dispersion"]["velocity_dispersion_bernardi"]
1451
+ if param_dict:
1452
+ param_dict.update(kwargs)
549
1453
  else:
550
- return inverse_transform_sampler(size, vd_inv_cdf[0], vd_inv_cdf[1])
1454
+ param_dict = kwargs
1455
+ identifier_dict.update(param_dict)
1456
+
1457
+ # setting up inputs for the interpolator
1458
+ sigma_array = np.linspace(
1459
+ identifier_dict['sigma_min'],
1460
+ identifier_dict['sigma_max'],
1461
+ identifier_dict["resolution"]
1462
+ )
1463
+ number_density_function = lambda sigma: phi_loc_bernardi(
1464
+ sigma,
1465
+ alpha=self.lens_param_samplers_params["velocity_dispersion"]['alpha'],
1466
+ beta=self.lens_param_samplers_params["velocity_dispersion"]['beta'],
1467
+ phistar=self.lens_param_samplers_params["velocity_dispersion"]['phistar'],
1468
+ sigmastar=self.lens_param_samplers_params["velocity_dispersion"]['sigmastar'],
1469
+ )
1470
+
1471
+ sigma_object = FunctionConditioning(
1472
+ function=number_density_function,
1473
+ x_array=sigma_array,
1474
+ param_dict_given=identifier_dict,
1475
+ directory=self.directory,
1476
+ sub_directory="velocity_dispersion",
1477
+ name=identifier_dict['name'],
1478
+ create_new=self.create_new_interpolator["velocity_dispersion"]['create_new'],
1479
+ create_function_inverse=False,
1480
+ create_function=True,
1481
+ create_pdf=True,
1482
+ create_rvs=True,
1483
+ callback='rvs',
1484
+ )
1485
+
1486
+ return sigma_object if get_attribute else sigma_object.rvs(size)
551
1487
 
552
1488
  def velocity_dispersion_ewoud(self, size, zl, get_attribute=False, **kwargs):
553
1489
  """
@@ -570,27 +1506,125 @@ class OpticalDepth():
570
1506
  Examples
571
1507
  --------
572
1508
  >>> from ler.lens_galaxy_population import OpticalDepth
573
- >>> od = OpticalDepth(sampler_priors=dict(velocity_dispersion="velocity_dispersion_ewoud"))
574
- >>> print(od.sample_velocity_dispersion(size=10, zl=0.5))
1509
+ >>> od = OpticalDepth(lens_param_samplers=dict(velocity_dispersion="velocity_dispersion_ewoud"))
1510
+ >>> print(od.velocity_dispersion(size=10, zl=0.5))
575
1511
  """
576
1512
 
577
- z_max = self.z_max
578
- z_min = self.z_min
579
- zlist = np.linspace(z_min, z_max, 100)
580
- vd_inv_cdf = self.vd_inv_cdf.copy()
1513
+ identifier_dict = {'name': "velocity_dispersion_ewoud"}
1514
+ identifier_dict['z_min'] = self.z_min
1515
+ identifier_dict['z_max'] = self.z_max
1516
+ identifier_dict['cosmology'] = self.cosmo
1517
+ identifier_dict['name'] = "velocity_dispersion_ewoud"
1518
+ identifier_dict['resolution'] = self.create_new_interpolator["velocity_dispersion"]["resolution"]
1519
+ param_dict = self.available_lens_prior_list_and_its_params["velocity_dispersion"]["velocity_dispersion_ewoud"].copy()
1520
+ if param_dict:
1521
+ param_dict.update(kwargs)
1522
+ else:
1523
+ param_dict = kwargs
1524
+ identifier_dict.update(param_dict)
1525
+
1526
+ # setting up inputs for the interpolator
1527
+ sigma_array = np.linspace(
1528
+ identifier_dict['sigma_min'],
1529
+ identifier_dict['sigma_max'],
1530
+ identifier_dict["resolution"]
1531
+ )
1532
+ number_density_function = lambda sigma, zl: phi(
1533
+ sigma,
1534
+ zl,
1535
+ alpha=self.lens_param_samplers_params["velocity_dispersion"]['alpha'],
1536
+ beta=self.lens_param_samplers_params["velocity_dispersion"]['beta'],
1537
+ phistar=self.lens_param_samplers_params["velocity_dispersion"]['phistar'],
1538
+ sigmastar=self.lens_param_samplers_params["velocity_dispersion"]['sigmastar'],
1539
+ )
581
1540
 
582
- @njit
583
- def sampler(size, zl, inv_cdf, zlist):
584
-
585
- idx = np.searchsorted(zlist, zl)
586
- return inverse_transform_sampler(size, inv_cdf[idx][0], inv_cdf[idx][1])
1541
+ zl_array = np.geomspace(self.z_min+0.001, self.z_max, 500) if self.z_min==0 else np.geomspace(self.z_min, self.z_max, 500)
1542
+
1543
+ sigma_object = FunctionConditioning(
1544
+ function=number_density_function,
1545
+ x_array=sigma_array,
1546
+ conditioned_y_array=zl_array,
1547
+ param_dict_given=identifier_dict,
1548
+ directory=self.directory,
1549
+ sub_directory="velocity_dispersion",
1550
+ name=identifier_dict['name'],
1551
+ create_new=self.create_new_interpolator["velocity_dispersion"]['create_new'],
1552
+ create_function_inverse=False,
1553
+ create_function=True,
1554
+ create_pdf=True,
1555
+ create_rvs=True,
1556
+ callback='rvs',
1557
+ )
587
1558
 
588
- if get_attribute:
589
- return njit(lambda size, zl: sampler(size, zl, vd_inv_cdf, zlist))
1559
+ return sigma_object if get_attribute else sigma_object.rvs(size, zl)
1560
+
1561
+ ##################
1562
+ # Lens functions #
1563
+ ##################
1564
+ def optical_depth_numerical(self, zs, get_attribute=False, **kwargs):
1565
+
1566
+ identifier_dict = {'name': "optical_depth_numerical"}
1567
+ identifier_dict['resolution'] = self.create_new_interpolator["optical_depth"]["resolution"]
1568
+ identifier_dict['z_min'] = self.z_min
1569
+ identifier_dict['z_max'] = self.z_max
1570
+ identifier_dict['cosmology'] = self.cosmo
1571
+ identifier_dict['velocity_dispersion'] = self.lens_param_samplers_params['velocity_dispersion']
1572
+ if identifier_dict['velocity_dispersion']: # if velocity_dispersion is not None
1573
+ identifier_dict['velocity_dispersion']['name'] = str(self.lens_param_samplers['velocity_dispersion'])
1574
+ identifier_dict['axis_ratio'] = self.lens_param_samplers_params['axis_ratio']
1575
+ if identifier_dict['axis_ratio']: # if axis_ratio is not None
1576
+ identifier_dict['axis_ratio']['name'] = str(self.lens_param_samplers['axis_ratio'])
1577
+ identifier_dict['axis_rotation_angle'] = self.lens_param_samplers_params['axis_rotation_angle']
1578
+ if identifier_dict['axis_rotation_angle']: # if axis_rotation_angle is not None
1579
+ identifier_dict['axis_rotation_angle']['name'] = str(self.lens_param_samplers['axis_rotation_angle'])
1580
+ identifier_dict['density_profile_slope'] = self.lens_param_samplers_params['density_profile_slope']
1581
+ if identifier_dict['density_profile_slope']: # if density_profile_slope is not None
1582
+ identifier_dict['density_profile_slope']['name'] = str(self.lens_param_samplers['density_profile_slope'])
1583
+ identifier_dict['external_shear'] = self.lens_param_samplers_params['external_shear']
1584
+ if identifier_dict['external_shear']: # if external_shear is not None
1585
+ identifier_dict['external_shear']['name'] = str(self.lens_param_samplers['external_shear'])
1586
+
1587
+ param_dict = self.available_lens_functions_and_its_params["optical_depth"]["optical_depth_numerical"]
1588
+ if param_dict:
1589
+ param_dict.update(kwargs)
590
1590
  else:
591
- return sampler(size, zl, vd_inv_cdf, zlist)
1591
+ param_dict = kwargs
1592
+ identifier_dict.update(param_dict)
1593
+
1594
+ z_min = self.z_min if self.z_min>0. else 0.001
1595
+ z_max = self.z_max
1596
+ resolution = identifier_dict['resolution']
1597
+ zs_array = np.geomspace(
1598
+ z_min,
1599
+ z_max,
1600
+ resolution
1601
+ )
1602
+
1603
+ def tau(zs):
1604
+ # self.lens_redshift.function gives cross-section
1605
+ integrand = lambda zl_, zs_: self.lens_redshift.function(np.array([zl_]), np.array([zs_]))[0]
1606
+ integral = [quad(integrand, 0.0, z, args=(z))[0] for z in zs]
1607
+ return integral
1608
+
1609
+ tau_object = FunctionConditioning(
1610
+ function=tau,
1611
+ x_array=zs_array,
1612
+ conditioned_y_array=None,
1613
+ param_dict_given=identifier_dict,
1614
+ directory=self.directory,
1615
+ sub_directory="optical_depth",
1616
+ name=identifier_dict['name'],
1617
+ create_new=self.create_new_interpolator["optical_depth"]["create_new"],
1618
+ create_function_inverse=False,
1619
+ create_function=True,
1620
+ create_pdf=True,
1621
+ create_rvs=True,
1622
+ callback='function',
1623
+ )
592
1624
 
593
- def cross_section_SIS(self, sigma, zl, zs):
1625
+ return tau_object if get_attribute else tau_object.function(zs)
1626
+
1627
+ def cross_section_sis(self, sigma, zl, zs, **kwargs):
594
1628
  """
595
1629
  Function to compute the SIS cross-section
596
1630
 
@@ -612,85 +1646,82 @@ class OpticalDepth():
612
1646
  --------
613
1647
  >>> from ler.lens_galaxy_population import OpticalDepth
614
1648
  >>> od = OpticalDepth()
615
- >>> print(od.cross_section_SIS(sigma=200., zl=0.5, zs=1.0))
1649
+ >>> print(od.cross_section_sis(sigma=200., zl=0.5, zs=1.0))
616
1650
  """
617
- zl = np.array([zl]).reshape(-1)
618
- zs = np.array([zs]).reshape(-1)
619
- Ds = self.angular_diameter_distance(zs)
620
- Dls = self.angular_diameter_distance_z1z2(zl, zs)
621
- theta_E = (
622
- 4.0 * np.pi * (sigma / 299792.458) ** 2 * Dls / (Ds)
623
- ) # Note: km/s for sigma; Dls, Ds are in Mpc
624
1651
 
1652
+ theta_E = self.compute_einstein_radii(sigma, zl, zs)
625
1653
  return np.pi * theta_E**2
626
1654
 
627
- def tau_zl_zs(self, zl, zs):
1655
+ def compute_einstein_radii(self, sigma, zl, zs):
628
1656
  """
629
- Function to compute the optical depth for a given lens redshift and source redshift
1657
+ Function to compute the Einstein radii of the lens galaxies
630
1658
 
631
1659
  Parameters
632
1660
  ----------
1661
+ sigma : `float`
1662
+ velocity dispersion of the lens galaxy
633
1663
  zl : `float`
634
- redshift of the lens galaxy
1664
+ lens redshifts
635
1665
  zs : `float`
636
- redshift of the source galaxy
1666
+ source redshifts
637
1667
 
638
1668
  Returns
639
1669
  -------
640
- tau : `float`
641
- optical depth
1670
+ theta_E : `float`
1671
+ Einstein radii of the lens galaxies in radians. Multiply by
642
1672
 
643
1673
  Examples
644
1674
  --------
645
- >>> from ler.lens_galaxy_population import OpticalDepth
646
- >>> od = OpticalDepth()
647
- >>> print(od.tau_zl_zs(zl=0.5, zs=1.0))
1675
+ >>> from ler.lens_galaxy_population import LensGalaxyParameterDistribution
1676
+ >>> lens = LensGalaxyParameterDistribution()
1677
+ >>> sigma = 200.0
1678
+ >>> zl = 0.5
1679
+ >>> zs = 1.0
1680
+ >>> lens.compute_einstein_radii(sigma, zl, zs)
648
1681
  """
649
- zl = np.array([zl]).reshape(-1)
650
- zs = np.array([zs]).reshape(-1)
651
- # size=5000 will take ~ 48s to run, with ewoud vd sampler
652
- try:
653
- sigma = self.sample_velocity_dispersion(size=5000)
654
- except:
655
- sigma = self.sample_velocity_dispersion(size=5000, zl=zl)
656
1682
 
657
- q = self.sample_axis_ratio(sigma) # if SIS, q=array of 1.0
658
- no = 8*1e-3*self.cosmo.h**3
659
- test = phi_cut_SIE(q)*self.cross_section_SIS(sigma=sigma, zl=zl, zs=zs)/(4*np.pi)*no*self.differential_comoving_volume(zl)
660
- # average
661
- return np.mean(test)
662
-
663
- def optical_depth_calculator(self, zs):
1683
+ # Compute the angular diameter distances
1684
+ Ds = self.angular_diameter_distance(zs)
1685
+ Dls = self.angular_diameter_distance_z1z2(zl, zs)
1686
+ # Compute the Einstein radii
1687
+ theta_E = (
1688
+ 4.0 * np.pi * (sigma / 299792.458) ** 2 * Dls / (Ds)
1689
+ ) # Note: km/s for sigma; Dls, Ds are in Mpc
1690
+
1691
+ return theta_E
1692
+
1693
+ def cross_section_sie_feixu(self, sigma, zl, zs, q, **kwargs):
664
1694
  """
665
- Function to compute the optical depth without multiprocessing. This is the integrated version of tau_zl_zs from z=0 to z=zs.
1695
+ Function to compute the SIE cross-section from Fei Xu et al. (2021)
666
1696
 
667
1697
  Parameters
668
1698
  ----------
1699
+ sigma : `float`
1700
+ velocity dispersion of the lens galaxy
1701
+ zl : `float`
1702
+ redshift of the lens galaxy
669
1703
  zs : `float`
670
- source redshifts
1704
+ redshift of the source galaxy
671
1705
 
672
1706
  Returns
673
1707
  -------
674
- tau : `float`
675
- optical depth
1708
+ cross_section : `float`
1709
+ SIE cross-section
676
1710
 
677
1711
  Examples
678
1712
  --------
679
1713
  >>> from ler.lens_galaxy_population import OpticalDepth
680
1714
  >>> od = OpticalDepth()
681
- >>> print(od.optical_depth_calculator(zs=1.0))
1715
+ >>> print(od.cross_section_sie_feixu(sigma=200., zl=0.5, zs=1.0, q=1.0))
682
1716
  """
683
1717
 
684
- zs = np.array([zs]).reshape(-1)
685
- tau_list = []
686
- for z in zs:
687
- tau_list.append(quad(self.tau_zl_zs, 0, z, args=(z))[0])
688
-
689
- return np.array(tau_list)
1718
+ # phi_cut_SIE already includes the factor of pi
1719
+ return phi_cut_SIE(q)*self.cross_section_sis(sigma=sigma, zl=zl, zs=zs)
690
1720
 
691
- def optical_depth_multiprocessing(self, zs):
1721
+ def optical_depth_epl_shear_hemanta(self, zs, get_attribute=False, **kwargs):
692
1722
  """
693
- Function to compute the optical depth with multiprocessing. This is the integrated version of optical depth from z=0 to z=zs.
1723
+ Function to compute the strong lensing optical depth (EPL with shear). \n
1724
+ LambdaCDM(H0=70, Om0=0.3, Ode0=0.7) was used to derive the following equation. This is the analytic version of optical depth from z=0 to z=zs.
694
1725
 
695
1726
  Parameters
696
1727
  ----------
@@ -700,37 +1731,66 @@ class OpticalDepth():
700
1731
  Returns
701
1732
  -------
702
1733
  tau : `float`
703
- optical depth
704
-
1734
+ strong lensing optical depth
1735
+
705
1736
  Examples
706
1737
  --------
707
1738
  >>> from ler.lens_galaxy_population import OpticalDepth
708
1739
  >>> od = OpticalDepth()
709
- >>> print(od.optical_depth_multiprocessing(zs=1.0))
1740
+ >>> print(od.optical_depth_epl_shear_lambdacdm(zs=1.0))
710
1741
  """
711
1742
 
712
- zs = np.array([zs]).reshape(-1)
713
- no = 8*1e-3*self.cosmo.h**3
714
- vd_inv_cdf = self.vd_inv_cdf
715
- splinedVcdz = self.splinedVcdz
716
- splineDa = self.splineDa
717
- idx = np.arange(len(zs))
718
- try:
719
- zl_list = self.zl_list
720
- input_params = [(zs[i], no, vd_inv_cdf, splinedVcdz, splineDa, idx[i], zl_list) for i in range(len(zs))]
721
- except:
722
- input_params = [(zs[i], no, vd_inv_cdf, splinedVcdz, splineDa, idx[i]) for i in range(len(zs))]
1743
+ # setting up input parameters for interpolation
1744
+ identifier_dict = {'name': 'optical_depth_numerical',
1745
+ 'resolution': 48,
1746
+ 'z_min': 0.0,
1747
+ 'z_max': 10.0,
1748
+ 'cosmology': LambdaCDM(H0=70, Om0=0.3, Ode0=0.7),
1749
+ 'velocity_dispersion': {'sigma_min': 100.0,
1750
+ 'sigma_max': 400.0,
1751
+ 'alpha': 0.94,
1752
+ 'beta': 1.85,
1753
+ 'phistar': 0.02099,
1754
+ 'sigmastar': 113.78,
1755
+ 'name': 'velocity_dispersion_ewoud'},
1756
+ 'axis_ratio': {'q_min': 0.2, 'q_max': 1.0, 'name': 'axis_ratio_rayleigh'},
1757
+ 'axis_rotation_angle': {'phi_min': 0.0,
1758
+ 'phi_max': 6.283185307179586,
1759
+ 'name': 'axis_rotation_angle_uniform'},
1760
+ 'density_profile_slope': {'mean': 1.99,
1761
+ 'std': 0.149,
1762
+ 'name': 'density_profile_slope_normal'},
1763
+ 'external_shear': {'mean': 0.0, 'std': 0.05, 'name': 'external_shear_normal'}
1764
+ }
1765
+
1766
+ print("Getting pre-computed optical depth for EPL+Shear lenses...")
723
1767
 
724
- # Create a pool of workers and parallelize the integration
725
- with Pool(processes=self.npool) as pool:
726
- result = list(pool.map(self.tau_mp_routine, input_params))
1768
+ zs_arr = np.geomspace(
1769
+ 0.001,
1770
+ 10,
1771
+ identifier_dict['resolution']
1772
+ )
1773
+ tau_arr = load_txt_from_module('ler', 'lens_galaxy_population.lens_param_data', 'optical_depth_epl_shear_vd_ewoud.txt')
727
1774
 
728
- result = np.array(result)
729
- tau_list = result[:,1][np.array(result[:,0], dtype=int)]
1775
+ tau_object = FunctionConditioning(
1776
+ function=tau_arr,
1777
+ x_array=zs_arr,
1778
+ conditioned_y_array=None,
1779
+ param_dict_given=identifier_dict,
1780
+ directory=self.directory,
1781
+ sub_directory="optical_depth",
1782
+ name=identifier_dict['name'],
1783
+ create_new=self.create_new_interpolator["optical_depth"]["create_new"],
1784
+ create_function_inverse=False,
1785
+ create_function=True,
1786
+ create_pdf=True,
1787
+ create_rvs=True,
1788
+ callback='function',
1789
+ )
730
1790
 
731
- return tau_list
1791
+ return tau_object if get_attribute else tau_object.function(zs)
732
1792
 
733
- def optical_depth_SIS_haris(self, zs):
1793
+ def optical_depth_sis_haris(self, zs, get_attribute=False, **kwargs):
734
1794
  """
735
1795
  Function to compute the strong lensing optical depth (SIS). \n
736
1796
  LambdaCDM(H0=70, Om0=0.3, Ode0=0.7) was used to derive the following equation. This is the analytic version of optical depth from z=0 to z=zs.
@@ -749,106 +1809,455 @@ class OpticalDepth():
749
1809
  --------
750
1810
  >>> from ler.lens_galaxy_population import OpticalDepth
751
1811
  >>> od = OpticalDepth()
752
- >>> print(od.optical_depth_SIS_haris(zs=1.0))
1812
+ >>> print(od.optical_depth_sis_haris(zs=1.0))
753
1813
  """
754
1814
 
755
- # z to luminosity_distance (luminosity_distance) conversion
756
- Dc = self.z_to_Dc(zs) * 1e-3 # 1e-3 converts Mpc to Gpc
1815
+ # # z to luminosity_distance (luminosity_distance) conversion
1816
+ # #Dc = self.z_to_Dc(zs) * 1e-3 # 1e-3 converts Mpc to Gpc
1817
+ # splineDc = self.splineDc
1818
+
1819
+
1820
+
1821
+ # if get_attribute:
1822
+ # return tau
1823
+ # else:
1824
+ # return tau(zs)
1825
+ identifier_dict = {'name': "optical_depth_sis_haris"}
1826
+ identifier_dict['z_min'] = self.z_min if self.z_min>0. else 0.001
1827
+ identifier_dict['z_max'] = self.z_max
1828
+ identifier_dict['cosmology'] = self.cosmo
1829
+ identifier_dict['resolution'] = self.create_new_interpolator["optical_depth"]["resolution"]
1830
+ param_dict = self.available_lens_functions_and_its_params["optical_depth"]["optical_depth_sis_haris"]
1831
+ if param_dict:
1832
+ param_dict.update(kwargs)
1833
+ else:
1834
+ param_dict = kwargs
1835
+ identifier_dict.update(param_dict)
1836
+
1837
+ zs_arr = np.geomspace(identifier_dict['z_min'], identifier_dict['z_max'], identifier_dict['resolution'])
1838
+
1839
+ def tau(zs):
1840
+ Dc = self.comoving_distance.function(zs)
1841
+ return (Dc * 1e-3 / 62.2) ** 3
1842
+
1843
+ tau_object = FunctionConditioning(
1844
+ function=tau,
1845
+ x_array=zs_arr,
1846
+ conditioned_y_array=None,
1847
+ param_dict_given=identifier_dict,
1848
+ directory=self.directory,
1849
+ sub_directory="optical_depth",
1850
+ name=identifier_dict['name'],
1851
+ create_new=self.create_new_interpolator["optical_depth"]["create_new"],
1852
+ create_function_inverse=False,
1853
+ create_function=True,
1854
+ create_pdf=True,
1855
+ create_rvs=True,
1856
+ callback='function',
1857
+ )
1858
+
1859
+ return tau_object if get_attribute else tau_object.function(zs)
1860
+
1861
+ #######################################
1862
+ # interpolated cross section function #
1863
+ #######################################
1864
+ def create_data_set(self, size=1000000):
1865
+ zs = np.random.uniform(0.01, 10.0, size-2)
1866
+ # inlcude z=0.01 and z=10.0
1867
+ zs = np.insert(zs, 0, 0.01)
1868
+ zs = np.append(zs, 10.0)
1869
+
1870
+ zl = np.zeros(size)
1871
+ for i in range(size):
1872
+ zl[i] = np.random.uniform(0.001, zs[i]-0.001)
1873
+
1874
+ sigma = np.random.uniform(self.lens_param_samplers_params["velocity_dispersion"]['sigma_min'], self.lens_param_samplers_params["velocity_dispersion"]['sigma_max'], size-2)
1875
+ # inlcude sigma_min and sigma_max
1876
+ sigma = np.insert(sigma, 0, self.lens_param_samplers_params["velocity_dispersion"]['sigma_min'])
1877
+ sigma = np.append(sigma, self.lens_param_samplers_params["velocity_dispersion"]['sigma_max'])
1878
+
1879
+ q = np.random.uniform(self.lens_param_samplers_params["axis_ratio"]['q_min'], self.lens_param_samplers_params["axis_ratio"]['q_max'], size-2)
1880
+ # inlcude q_min and q_max
1881
+ q = np.insert(q, 0, self.lens_param_samplers_params["axis_ratio"]['q_min'])
1882
+ q = np.append(q, self.lens_param_samplers_params["axis_ratio"]['q_max'])
1883
+
1884
+ phi = np.random.uniform(0.0, 2 * np.pi, size-2)
1885
+ # inlcude 0 and 2 * np.pi
1886
+ phi = np.insert(phi, 0, 0.0)
1887
+ phi = np.append(phi, 2 * np.pi)
1888
+
1889
+ e1, e2 = phi_q2_ellipticity_hemanta(phi, q)
1890
+ gamma1, gamma2 = np.random.normal(loc=0, scale=0.05, size=(2, size))
1891
+ gamma = np.random.normal(loc=2.0, scale=0.2, size=size)
1892
+ # Da_zs = od_epl_ewoud.angular_diameter_distance(np.array([zs]))[0]
1893
+ # einstein radius
1894
+ Ds = self.angular_diameter_distance.function(zs)
1895
+ Dls = self.angular_diameter_distance_z1z2(zl, zs)
1896
+ theta_E = (
1897
+ 4.0 * np.pi * (sigma / 299792.458) ** 2 * Dls / (Ds)
1898
+ ) # Note: km/s for sigma; Dls, Ds are in Mpc
1899
+
1900
+ return theta_E, e1, e2, gamma, gamma1, gamma2, q
1901
+
1902
+ def cross_section_caustic_area(self, theta_E, e1, e2, gamma, gamma1, gamma2):
1903
+
1904
+ size = theta_E.shape[0]
1905
+ area_list = np.zeros(size)
1906
+ for i in range(size):
1907
+ kwargs_lens = [
1908
+ {
1909
+ "theta_E": theta_E[i],
1910
+ "e1": e1[i],
1911
+ "e2": e2[i],
1912
+ "gamma": gamma[i],
1913
+ "center_x": 0.0,
1914
+ "center_y": 0.0,
1915
+ },
1916
+ {
1917
+ "gamma1": gamma1[i],
1918
+ "gamma2": gamma2[i],
1919
+ "ra_0": 0,
1920
+ "dec_0": 0,
1921
+ },
1922
+ ]
1923
+
1924
+ caustic_double_points = caustics_epl_shear(
1925
+ kwargs_lens, return_which="double", maginf=-100
1926
+ )
1927
+ caustic = np.logical_not(np.isnan(caustic_double_points).any())
1928
+
1929
+ # If there is a nan, caustic=False, draw a new gamma
1930
+ if caustic:
1931
+ area_list[i] = (Polygon(caustic_double_points.T).area)
1932
+
1933
+ return np.array(area_list)
1934
+
1935
+ def cross_section_caustic_area_unit(self, e1, e2, gamma, gamma1, gamma2):
1936
+
1937
+ size = e1.shape[0]
1938
+ area_list = np.zeros(size)
1939
+ for i in range(size):
1940
+ kwargs_lens = [
1941
+ {
1942
+ "theta_E": 1.0,
1943
+ "e1": e1[i],
1944
+ "e2": e2[i],
1945
+ "gamma": gamma[i],
1946
+ "center_x": 0.0,
1947
+ "center_y": 0.0,
1948
+ },
1949
+ {
1950
+ "gamma1": gamma1[i],
1951
+ "gamma2": gamma2[i],
1952
+ "ra_0": 0,
1953
+ "dec_0": 0,
1954
+ },
1955
+ ]
1956
+
1957
+ caustic_double_points = caustics_epl_shear(
1958
+ kwargs_lens, return_which="double", maginf=-100
1959
+ )
1960
+ caustic = np.logical_not(np.isnan(caustic_double_points).any())
757
1961
 
758
- return (Dc / 62.2) ** 3 # 62.2 is the critical density in Gpc
1962
+ # If there is a nan, caustic=False, draw a new gamma
1963
+ if caustic:
1964
+ area_list[i] = (Polygon(caustic_double_points.T).area)
1965
+
1966
+ return np.array(area_list)
759
1967
 
760
- def create_lookup_table_fuction(self, z_max):
1968
+ def create_cross_section_function(self):
1969
+
1970
+ param_dict_given = dict(
1971
+ z_min=self.z_min,
1972
+ z_max=self.z_max,
1973
+ cosmology=self.cosmo,
1974
+ resolution=self.create_new_interpolator["cross_section"]["resolution"],
1975
+ sigma_min=self.lens_param_samplers_params["velocity_dispersion"]['sigma_min'],
1976
+ sigma_max=self.lens_param_samplers_params["velocity_dispersion"]['sigma_max'],
1977
+ q_min = self.lens_param_samplers_params["axis_ratio"]['q_min'],
1978
+ q_max = self.lens_param_samplers_params["axis_ratio"]['q_max'],
1979
+ )
1980
+
1981
+ file_path, it_exist = interpolator_pickle_path(
1982
+ param_dict_given=param_dict_given,
1983
+ directory=self.directory,
1984
+ sub_directory="cross_section_function",
1985
+ interpolator_name="cross_section_function",
1986
+ )
1987
+
1988
+ if (self.create_new_interpolator["cross_section"]["create_new"] is True) or (it_exist is False):
1989
+
1990
+ print(f"Interpolated cross section function will be created at {file_path}")
1991
+ print("Creating interpolated cross section function with sklearn NearestNeighbors, and this will be use for cross section calculation")
1992
+
1993
+ size_interpolation=1000
1994
+ theta_E, e1, e2, gamma, gamma1, gamma2, _ = self.create_data_set(size_interpolation)
1995
+ # sort
1996
+ idx = np.argsort(theta_E)
1997
+ theta_E, e1, e2, gamma, gamma1, gamma2 = theta_E[idx], e1[idx], e2[idx], gamma[idx], gamma1[idx], gamma2[idx]
1998
+ values1 = self.cross_section_caustic_area(theta_E, e1, e2, gamma, gamma1, gamma2)
1999
+ values2 = self.cross_section_caustic_area_unit(e1, e2, gamma, gamma1, gamma2)
2000
+
2001
+ x = np.pi*theta_E**2
2002
+
2003
+ idx_cs = (values2 > 0.0) & (values1 > 0.0) & (x > 0.0)
2004
+ value3 = values1[idx_cs]/values2[idx_cs]
2005
+ x = x[idx_cs]
2006
+ spline_coeff = CubicSpline(x, value3).c
2007
+
2008
+ # K nearest neighbors
2009
+ size = self.create_new_interpolator["cross_section"]["resolution"]
2010
+ iter_ = np.arange(size)
2011
+ _, e1, e2, gamma, gamma1, gamma2, _ = self.create_data_set(size)
2012
+ points = np.column_stack((e1, e2, gamma, gamma1, gamma2, iter_))
2013
+ from .mp import cross_section_unit_mp
2014
+ area_list = np.ones(size)
2015
+ with Pool(processes=8) as pool:
2016
+ for result in tqdm(
2017
+ pool.imap_unordered(cross_section_unit_mp, points),
2018
+ total=size,
2019
+ ncols=100,
2020
+ disable=False,
2021
+ ):
2022
+ (
2023
+ iter_i,
2024
+ area,
2025
+ ) = result
2026
+ area_list[int(iter_i)] = area
2027
+
2028
+ values = np.array(area_list)
2029
+ points = points[:, :-1]
2030
+
2031
+ from sklearn.neighbors import NearestNeighbors
2032
+
2033
+ # Fit nearest neighbor model
2034
+ nbrs = NearestNeighbors(n_neighbors=10).fit(points)
2035
+
2036
+ #print(mean_squared_error(values1, y_pred))
2037
+
2038
+ # save the cross_section_function as pickle
2039
+ save_pickle(file_path, [nbrs,values,spline_coeff,x])
2040
+ self.nbrs = nbrs
2041
+ self.values = values
2042
+ self.cross_section_spline = spline_coeff
2043
+ self.sis_area_array = x
2044
+
2045
+ else:
2046
+ from ..utils import load_pickle
2047
+ print(f"Interpolated cross section function loaded from {file_path}")
2048
+ load_ = load_pickle(file_path)
2049
+ self.nbrs, self.values, self.cross_section_spline, self.sis_area_array = load_
2050
+
2051
+ def interpolated_cross_section_function(self, theta_E, e1, e2, gamma, gamma1, gamma2, get_attribute=False, **kwargs):
2052
+ """
2053
+ Function to compute the cross-section correction factor
2054
+ """
2055
+ nbrs = self.nbrs
2056
+ values = self.values
2057
+
2058
+ points_ = np.array([e1, e2, gamma, gamma1, gamma2]).T
2059
+ _, indices = nbrs.kneighbors(points_)
2060
+ new_values_nn1 = np.mean(values[indices], axis=1)
2061
+
2062
+ theta_E_correction = cubic_spline_interpolator(np.pi*theta_E**2, self.cross_section_spline, self.sis_area_array)
2063
+
2064
+ return new_values_nn1*theta_E_correction
2065
+
2066
+ ##########################
2067
+ # Cosmological functions #
2068
+ ##########################
2069
+ def create_lookup_table_fuction(self):
761
2070
  """
762
2071
  Functions to create lookup tables
763
2072
  1. Redshift to co-moving distance.
764
2073
  2. Co-moving distance to redshift.
765
2074
  3. Redshift to angular diameter distance.
766
2075
 
2076
+ """
2077
+
2078
+ z_max = self.z_max
2079
+ z_min = 0.001 if self.z_min == 0. else self.z_min
2080
+ # for co-moving distance
2081
+ resolution = self.create_new_interpolator["comoving_distance"]["resolution"]
2082
+ create_new = self.create_new_interpolator["comoving_distance"]["create_new"]
2083
+ zs = np.geomspace(z_min, z_max, resolution)
2084
+ Dc = self.cosmo.comoving_distance(zs).value # co-moving distance in Mpc
2085
+ self.comoving_distance = FunctionConditioning(
2086
+ function=Dc,
2087
+ x_array=zs,
2088
+ conditioned_y_array=None,
2089
+ param_dict_given=dict(z_min=z_min, z_max=z_max, cosmology=self.cosmo, resolution=resolution, details="comoving_distance from astropy.cosmology"),
2090
+ directory=self.directory,
2091
+ sub_directory="comoving_distance",
2092
+ name="comoving_distance",
2093
+ create_new=create_new,
2094
+ create_function_inverse=True,
2095
+ create_function=True,
2096
+ create_pdf=False,
2097
+ create_rvs=False,
2098
+ callback='function',
2099
+ )
2100
+ self.comoving_distance.__doc__ = """
2101
+ Redshift to comoving distance conversion.
2102
+
767
2103
  Parameters
768
2104
  ----------
769
- z_max : `float`
770
- maximum redshift of the lens galaxy population
2105
+ zs : `numpy.ndarray` or `float`
2106
+ Source redshifts
2107
+
2108
+ Returns
2109
+ ----------
2110
+ comoving_distance : `numpy.ndarray`
2111
+ comoving distance in Mpc
2112
+
2113
+ Examples
2114
+ ----------
2115
+ >>> from ler.len_galaxy_population import OpticalDepth
2116
+ >>> ler = OpticalDepth() # with default LambdaCDM(H0=70, Om0=0.3, Ode0=0.7)
2117
+ >>> comoving_distance = ler.comoving_distance(1.)
2118
+ >>> comoving_distance = ler.comoving_distance.function(np.array([1., 2.]))
2119
+ >>> redshift = ler.comoving_distance.function_inverse(np.array([100., 200.]))
771
2120
  """
772
2121
 
773
- Dc = lambda z_: self.cosmo.comoving_distance(z_).value # co-moving distance in Mpc
774
- resolution = self.c_n_i["z_to_Dc"]["resolution"]
775
- create_new = self.c_n_i["z_to_Dc"]["create_new"]
776
- splineDc = interpolator_from_pickle(
777
- param_dict_given= dict(z_min=0.001, z_max=z_max, cosmology=self.cosmo, resolution=resolution),
778
- directory=self.directory,
779
- sub_directory="z_to_Dc",
780
- name="z_to_Dc",
781
- x = np.geomspace(0.001, z_max, resolution),
782
- pdf_func= Dc,
783
- conditioned_y=None,
784
- dimension=1,
785
- category="function",
786
- create_new= create_new,
787
- )
788
- self.splineDc = splineDc
789
- self.z_to_Dc = njit(lambda z_: cubic_spline_interpolator(z_, splineDc[0], splineDc[1]))
790
-
791
- # for co-moving distance to redshift
792
- resolution = self.c_n_i["Dc_to_z"]["resolution"]
793
- create_new = self.c_n_i["Dc_to_z"]["create_new"]
794
- splineDcInv = interpolator_from_pickle(
795
- param_dict_given= dict(z_min=0.001, z_max=z_max, cosmology=self.cosmo, resolution=resolution),
2122
+ # for angular diameter distance
2123
+ resolution = self.create_new_interpolator["angular_diameter_distance"]["resolution"]
2124
+ create_new = self.create_new_interpolator["angular_diameter_distance"]["create_new"]
2125
+ zs = np.geomspace(z_min, z_max, resolution)
2126
+ Da = self.cosmo.angular_diameter_distance(zs).value
2127
+ self.angular_diameter_distance = FunctionConditioning(
2128
+ function=Da,
2129
+ x_array=zs,
2130
+ conditioned_y_array=None,
2131
+ param_dict_given=dict(z_min=z_min, z_max=z_max, cosmology=self.cosmo, resolution=resolution, details="angular_diameter_distance from astropy.cosmology"),
796
2132
  directory=self.directory,
797
- sub_directory="Dc_to_z",
798
- name="Dc_to_z",
799
- x = np.geomspace(0.001, z_max, resolution),
800
- pdf_func= Dc,
801
- conditioned_y=None,
802
- dimension=1,
803
- category="function_inverse",
2133
+ sub_directory="angular_diameter_distance",
2134
+ name="angular_diameter_distance",
804
2135
  create_new=create_new,
2136
+ create_function_inverse=False,
2137
+ create_function=True,
2138
+ create_pdf=False,
2139
+ create_rvs=False,
2140
+ callback='function',
805
2141
  )
806
- self.splineDcInv = splineDcInv
807
- self.Dc_to_z = njit(lambda Dc_: cubic_spline_interpolator(Dc_, splineDcInv[0], splineDcInv[1]))
2142
+ self.angular_diameter_distance.__doc__ = """
2143
+ Redshift to angular diameter distance conversion.
808
2144
 
809
- # for angular diameter distance
810
- resolution = self.c_n_i["angular_diameter_distance"]["resolution"]
811
- create_new = self.c_n_i["angular_diameter_distance"]["create_new"]
812
- Da = lambda z_: self.cosmo.angular_diameter_distance(z_).value
813
- splineDa = interpolator_from_pickle(
814
- param_dict_given= dict(z_min=0.001, z_max=z_max, cosmology=self.cosmo, resolution=resolution),
815
- directory=self.directory,
816
- sub_directory="angular_diameter_distance",
817
- name="angular_diameter_distance",
818
- x = np.geomspace(0.001, z_max, resolution),
819
- pdf_func= Da,
820
- conditioned_y=None,
821
- dimension=1,
822
- category="function",
823
- create_new=create_new,
824
- )
825
- self.splineDa = splineDa
826
- angular_diameter_distance = njit(lambda z_: cubic_spline_interpolator(z_, splineDa[0], splineDa[1]))
827
- self.angular_diameter_distance = angular_diameter_distance
2145
+ Parameters
2146
+ ----------
2147
+ zs : `numpy.ndarray` or `float`
2148
+ Source redshifts
2149
+
2150
+ Returns
2151
+ ----------
2152
+ angular_diameter_distance : `numpy.ndarray`
2153
+ angular diameter distance in Mpc
2154
+
2155
+ Examples
2156
+ ----------
2157
+ >>> from ler.len_galaxy_population import OpticalDepth
2158
+ >>> ler = OpticalDepth() # with default LambdaCDM(H0=70, Om0=0.3, Ode0=0.7)
2159
+ >>> angular_diameter_distance = ler.angular_diameter_distance(1.)
2160
+ >>> angular_diameter_distance = ler.angular_diameter_distance.function(np.array([1., 2.]))
2161
+ >>> redshift = ler.angular_diameter_distance.function_inverse(np.array([100., 200.]))
2162
+ """
828
2163
 
829
2164
  # for angular diameter distance between two redshifts
830
- self.angular_diameter_distance_z1z2 = njit(lambda zl0, zs0: (angular_diameter_distance(zs0)*(1.+zs0) - angular_diameter_distance(zl0)*(1.+zl0))/(1.+zs0))
2165
+ angular_diameter_distance_z1z2 = lambda zl0, zs0: (self.angular_diameter_distance.function(zs0)*(1.+zs0) - self.angular_diameter_distance.function(zl0)*(1.+zl0))/(1.+zs0)
2166
+ self.angular_diameter_distance_z1z2 = FunctionConditioning(
2167
+ function=None,
2168
+ x_array=None,
2169
+ param_dict_given=dict(z_min=z_min, z_max=z_max, cosmology=self.cosmo, resolution=resolution, details="angular_diameter_distance_z1z2 from astropy.cosmology"),
2170
+ create_function=angular_diameter_distance_z1z2,
2171
+ callback='function',
2172
+ )
2173
+ self.angular_diameter_distance_z1z2.__doc__ = """
2174
+ Redshift to angular diameter distance conversion.
2175
+
2176
+ Parameters
2177
+ ----------
2178
+ zl0 : `numpy.ndarray` or `float`
2179
+ Lens redshifts
2180
+ zs0 : `numpy.ndarray` or `float`
2181
+ Source redshifts
2182
+
2183
+ Returns
2184
+ ----------
2185
+ angular_diameter_distance_z1z2 : `numpy.ndarray`
2186
+ angular diameter distance in Mpc
2187
+
2188
+ Examples
2189
+ ----------
2190
+ >>> from ler.len_galaxy_population import OpticalDepth
2191
+ >>> ler = OpticalDepth() # with default LambdaCDM(H0=70, Om0=0.3, Ode0=0.7)
2192
+ >>> angular_diameter_distance_z1z2 = ler.angular_diameter_distance_z1z2(1., 2.)
2193
+ >>> angular_diameter_distance_z1z2 = ler.angular_diameter_distance_z1z2.function(np.array([1., 2.]), np.array([1., 2.]))
2194
+ """
831
2195
 
832
2196
  # get differential co-moving volume interpolator
833
- resolution = self.c_n_i["differential_comoving_volume"]["resolution"]
834
- create_new = self.c_n_i["differential_comoving_volume"]["create_new"]
835
- splinedVcdz = interpolator_from_pickle(
836
- param_dict_given= dict(z_min=0.001, z_max=z_max, cosmology=self.cosmo, resolution=resolution),
2197
+ resolution = self.create_new_interpolator["differential_comoving_volume"]["resolution"]
2198
+ create_new = self.create_new_interpolator["differential_comoving_volume"]["create_new"]
2199
+ zs = np.geomspace(z_min, z_max, resolution)
2200
+ dVcdz = self.cosmo.differential_comoving_volume(zs).value * 4 * np.pi # volume of shell in Mpc^3
2201
+ self.differential_comoving_volume = FunctionConditioning(
2202
+ function=dVcdz,
2203
+ x_array=zs,
2204
+ conditioned_y_array=None,
2205
+ param_dict_given=dict(z_min=z_min, z_max=z_max, cosmology=self.cosmo, resolution=resolution, details="differential_comoving_volume from astropy.cosmology"),
837
2206
  directory=self.directory,
838
- sub_directory="differential_comoving_volume",
2207
+ sub_directory="differential_comoving_volume",
839
2208
  name="differential_comoving_volume",
840
- x = np.geomspace(0.001, z_max, resolution),
841
- pdf_func= lambda z_: self.cosmo.differential_comoving_volume(z_).value * 4 * np.pi,
842
- conditioned_y=None,
843
- dimension=1,
844
- category="function",
845
2209
  create_new=create_new,
2210
+ create_function_inverse=False,
2211
+ create_function=True,
2212
+ create_pdf=False,
2213
+ create_rvs=False,
2214
+ callback='function',
846
2215
  )
847
- self.splinedVcdz = splinedVcdz
848
- self.differential_comoving_volume = njit(lambda z_: cubic_spline_interpolator(z_, splinedVcdz[0], splinedVcdz[1]))
2216
+ self.differential_comoving_volume.__doc__ = """
2217
+ Redshift to differential comoving volume conversion.
2218
+
2219
+ Parameters
2220
+ ----------
2221
+ zs : `numpy.ndarray` or `float`
2222
+ Source redshifts
2223
+
2224
+ Returns
2225
+ ----------
2226
+ differential_comoving_volume : `numpy.ndarray`
2227
+ differential comoving volume in Mpc^3
2228
+
2229
+ Examples
2230
+ ----------
2231
+ >>> from ler.len_galaxy_population import OpticalDepth
2232
+ >>> ler = OpticalDepth() # with default LambdaCDM(H0=70, Om0=0.3, Ode0=0.7)
2233
+ >>> differential_comoving_volume = ler.differential_comoving_volume(1.)
2234
+ >>> differential_comoving_volume = ler.differential_comoving_volume.function(np.array([1., 2.]))
2235
+ """
2236
+
2237
+ ########################
2238
+ # Attribute properties #
2239
+ ########################
2240
+ # @property
2241
+ # def cross_section(self):
2242
+ # return self._cross_section
2243
+
2244
+ # @cross_section.setter
2245
+ # def cross_section(self, input_function):
2246
+ # if input_function in self.available_cross_section_list_and_its_params:
2247
+ # print(f"using ler available cross section function : {input_function}")
2248
+ # self._cross_section = getattr(self, input_function)(zs=None, get_attribute=True)
2249
+ # elif callable(input_function):
2250
+ # print(f"using user provided custom cross section function")
2251
+ # self._cross_section = input_function
2252
+ # elif isinstance(input_function, object):
2253
+ # print(f"using user provided custom cross section class/object")
2254
+ # self._cross_section = input_function
2255
+ # else:
2256
+ # raise ValueError("input_function not in available_cross_section_list_and_its_params")
2257
+
849
2258
 
850
2259
  @property
851
- def strong_lensing_optical_depth(self):
2260
+ def optical_depth(self):
852
2261
  """
853
2262
  Function to compute the strong lensing optical depth.
854
2263
 
@@ -866,26 +2275,34 @@ class OpticalDepth():
866
2275
  --------
867
2276
  >>> from ler.lens_galaxy_population import OpticalDepth
868
2277
  >>> od = OpticalDepth()
869
- >>> print(od.strong_lensing_optical_depth(np.array([0.1,0.2,0.3])))
2278
+ >>> print(od.optical_depth(np.array([0.1,0.2,0.3])))
870
2279
  """
871
2280
 
872
- return self._strong_lensing_optical_depth
2281
+ return self._optical_depth
873
2282
 
874
- @strong_lensing_optical_depth.setter
875
- def strong_lensing_optical_depth(self, input_):
876
- if callable(input_):
877
- self._strong_lensing_optical_depth = input_
2283
+ @optical_depth.setter
2284
+ def optical_depth(self, input_function):
2285
+ if input_function in self.available_lens_functions_and_its_params['optical_depth']:
2286
+ print(f"using ler available optical depth function : {input_function}")
2287
+ args = self.lens_functions_params["optical_depth"]
2288
+ if args is None:
2289
+ self._optical_depth = getattr(self, input_function)(zs=None, get_attribute=True)
2290
+ else:
2291
+ self._optical_depth = getattr(self, input_function)(zs=None, get_attribute=True, **args)
2292
+ elif callable(input_function):
2293
+ print(f"using user provided custom optical depth function")
2294
+ self._optical_depth = FunctionConditioning(function=None, x_array=None, create_function=input_function)
2295
+ elif isinstance(input_function, object):
2296
+ print(f"using user provided custom optical depth class/object")
2297
+ self._optical_depth = input_function
878
2298
  else:
879
- # input can be function or spline interpolator array
880
- try:
881
- self._strong_lensing_optical_depth = njit(lambda zs: cubic_spline_interpolator(zs, input_[0], input_[1]))
882
- except:
883
- raise ValueError("strong_lensing_optical_depth must be a callable function or spline interpolator array.")
2299
+ raise ValueError("input_function not in available_lens_functions_and_its_params['optical_depth']")
2300
+
884
2301
 
885
2302
  @property
886
- def sample_velocity_dispersion(self):
2303
+ def velocity_dispersion(self):
887
2304
  """
888
- Function to sample velocity dispersion. `zl` is required only if velocity dispersion sampler is redshift dependent.
2305
+ Class object to sample velocity dispersion. `zl` is required only if velocity dispersion sampler is redshift dependent.
889
2306
 
890
2307
  Parameters
891
2308
  ----------
@@ -903,20 +2320,34 @@ class OpticalDepth():
903
2320
  --------
904
2321
  >>> from ler.lens_galaxy_population import OpticalDepth
905
2322
  >>> od = OpticalDepth()
906
- >>> print(od.sample_velocity_dispersion(size=10))
2323
+ >>> print(od.velocity_dispersion(size=10))
907
2324
  """
908
2325
 
909
- return self._sample_velocity_dispersion
2326
+ return self._velocity_dispersion
910
2327
 
911
- @sample_velocity_dispersion.setter
912
- def sample_velocity_dispersion(self, prior):
913
- try:
914
- self._sample_velocity_dispersion = getattr(self, prior)(size=None, zl=None, get_attribute=True)
915
- except:
916
- self._sample_velocity_dispersion = prior
2328
+ @velocity_dispersion.setter
2329
+ def velocity_dispersion(self, prior):
2330
+
2331
+ if prior in self.available_lens_prior_list_and_its_params["velocity_dispersion"]:
2332
+ print(f"using ler available velocity dispersion function : {prior}")
2333
+ args = self.lens_param_samplers_params["velocity_dispersion"]
2334
+ if prior=="velocity_dispersion_choi":
2335
+ prior = "velocity_dispersion_bernardi"
2336
+ if args is None:
2337
+ self._velocity_dispersion = getattr(self, prior)(size=None, zl=None, get_attribute=True)
2338
+ else:
2339
+ self._velocity_dispersion = getattr(self, prior)(size=None, zl=None, get_attribute=True, **args)
2340
+ elif callable(prior):
2341
+ print("using user provided custom velocity_dispersion function")
2342
+ self._velocity_dispersion = FunctionConditioning(function=None, x_array=None, create_rvs=prior)
2343
+ elif isinstance(prior, object):
2344
+ print("using user provided custom velocity_dispersion class/object")
2345
+ self._velocity_dispersion = prior
2346
+ else:
2347
+ raise ValueError(f"velocity_dispersion prior not in available_lens_prior_list_and_its_params['velocity_dispersion']")
917
2348
 
918
2349
  @property
919
- def sample_axis_ratio(self):
2350
+ def axis_ratio(self):
920
2351
  """
921
2352
  Function to sample axis ratio from rayleigh distribution with given velocity dispersion.
922
2353
 
@@ -934,57 +2365,258 @@ class OpticalDepth():
934
2365
  --------
935
2366
  >>> from ler.lens_galaxy_population import OpticalDepth
936
2367
  >>> od = OpticalDepth()
937
- >>> print(od.sample_axis_ratio(sigma=200.))
2368
+ >>> print(od.axis_ratio(sigma=200.))
938
2369
  """
939
2370
 
940
- return self._sample_axis_ratio
941
-
942
- @sample_axis_ratio.setter
943
- def sample_axis_ratio(self, prior):
944
- try:
945
- args = self.sampler_priors_params["axis_ratio"]
946
- self._sample_axis_ratio = getattr(self, prior)(sigma=None, get_attribute=True, param=args)
947
- except:
948
- self._sample_axis_ratio = prior
2371
+ return self._axis_ratio
949
2372
 
2373
+ @axis_ratio.setter
2374
+ def axis_ratio(self, prior):
2375
+ if prior in self.available_lens_prior_list_and_its_params["axis_ratio"]:
2376
+ print(f"using ler available axis_ratio function : {prior}")
2377
+ args = self.lens_param_samplers_params["axis_ratio"]
2378
+ if args is None:
2379
+ self._axis_ratio = getattr(self, prior)(size=None,sigma=None, get_attribute=True)
2380
+ else:
2381
+ self._axis_ratio = getattr(self, prior)(size=None, sigma=None, get_attribute=True, **args)
2382
+ elif callable(prior):
2383
+ print("using user provided custom axis_ratio function")
2384
+ self._axis_ratio = FunctionConditioning(function=None, x_array=None, create_rvs=prior)
2385
+ elif isinstance(prior, object):
2386
+ print("using user provided custom axis_ratio class/object")
2387
+ self._axis_ratio = prior
2388
+ else:
2389
+ raise ValueError("prior not in available_lens_prior_list_and_its_params['axis_ratio']")
2390
+
950
2391
  @property
951
- def available_velocity_dispersion_list_and_its_params(self):
952
- """
953
- Function to list all available velocity dispersion sampler and its parameters.
954
- """
955
- self._available_velocity_dispersion_list_and_its_params = dict(
956
- velocity_dispersion_haris=dict(a=2.32 / 2.67, c=2.67),
957
- velocity_dispersion_gengamma=dict(a=2.32 / 2.67, c=2.67),
958
- velocity_dispersion_bernardi=None,
959
- velocity_dispersion_ewoud=None,
960
- )
961
-
962
- return self._available_velocity_dispersion_list_and_its_params
2392
+ def lens_redshift(self):
2393
+ return self._lens_redshift
2394
+
2395
+ @lens_redshift.setter
2396
+ def lens_redshift(self, prior):
2397
+ if prior in self.available_lens_prior_list_and_its_params["lens_redshift"]:
2398
+ print(f"using ler available lens_redshift function : {prior}")
2399
+ args = self.lens_param_samplers_params["lens_redshift"]
2400
+ if args is None:
2401
+ self._lens_redshift = getattr(self, prior)(size=None, zs=None, get_attribute=True)
2402
+ else:
2403
+ self._lens_redshift = getattr(self, prior)(size=None, zs=None, get_attribute=True, **args)
2404
+ elif callable(prior):
2405
+ print("using user provided custom lens_redshift function")
2406
+ self._lens_redshift = FunctionConditioning(function=None, x_array=None, create_rvs=prior)
2407
+ elif isinstance(prior, object):
2408
+ print("using user provided custom lens_redshift class/object")
2409
+ self._lens_redshift = prior
2410
+ else:
2411
+ raise ValueError("prior not in available_lens_prior_list_and_its_params['lens_redshift']")
2412
+
2413
+ @property
2414
+ def axis_rotation_angle(self):
2415
+ return self._axis_rotation_angle
2416
+
2417
+ @axis_rotation_angle.setter
2418
+ def axis_rotation_angle(self, prior):
2419
+ if prior in self.available_lens_prior_list_and_its_params["axis_rotation_angle"]:
2420
+ print(f"using ler available axis_rotation_angle function : {prior}")
2421
+ args = self.lens_param_samplers_params["axis_rotation_angle"]
2422
+ if args is None:
2423
+ self._axis_rotation_angle = getattr(self, prior)(size=None, get_attribute=True)
2424
+ else:
2425
+ self._axis_rotation_angle = getattr(self, prior)(size=None, get_attribute=True, **args)
2426
+ elif callable(prior):
2427
+ print("using user provided custom axis_rotation_angle function")
2428
+ self._axis_rotation_angle = FunctionConditioning(function=None, x_array=None, create_rvs=prior)
2429
+ elif isinstance(prior, object):
2430
+ print("using user provided custom axis_rotation_angle class/object")
2431
+ self._axis_rotation_angle = prior
2432
+ else:
2433
+ raise ValueError("prior not in available_lens_prior_list_and_its_params['axis_rotation_angle']")
2434
+
2435
+ @property
2436
+ def external_shear(self):
2437
+ return self._external_shear
2438
+
2439
+ @external_shear.setter
2440
+ def external_shear(self, prior):
2441
+ if prior in self.available_lens_prior_list_and_its_params["external_shear"]:
2442
+ print(f"using ler available external_shear function : {prior}")
2443
+ args = self.lens_param_samplers_params["external_shear"]
2444
+ if args is None:
2445
+ self._external_shear = getattr(self, prior)(size=None, get_attribute=True)
2446
+ else:
2447
+ self._external_shear = getattr(self, prior)(size=None, get_attribute=True, **args)
2448
+ elif callable(prior):
2449
+ print("using user provided custom external_shear function")
2450
+ self._external_shear = FunctionConditioning(function=None, x_array=None, create_rvs=prior)
2451
+ elif isinstance(prior, object):
2452
+ print("using user provided custom external_shear class/object")
2453
+ self._external_shear = prior
2454
+ else:
2455
+ raise ValueError("prior not in available_lens_prior_list_and_its_params['external_shear']")
2456
+
2457
+ @property
2458
+ def external_shear_sl(self):
2459
+ return self._external_shear_sl
2460
+
2461
+ @external_shear_sl.setter
2462
+ def external_shear_sl(self, prior):
2463
+ if prior in self.available_lens_prior_list_and_its_params["external_shear_sl"]:
2464
+ print(f"using ler available external_shear_sl function : {prior}")
2465
+ args = self.lens_param_samplers_params["external_shear_sl"]
2466
+ if args is None:
2467
+ self._external_shear_sl = getattr(self, prior)(size=None, get_attribute=True)
2468
+ else:
2469
+ self._external_shear_sl = getattr(self, prior)(size=None, get_attribute=True, **args)
2470
+ elif callable(prior):
2471
+ print("using user provided custom external_shear_sl function")
2472
+ self._external_shear_sl = FunctionConditioning(function=None, x_array=None, create_rvs=prior)
2473
+ elif isinstance(prior, object):
2474
+ print("using user provided custom external_shear_sl class/object")
2475
+ self._external_shear_sl = prior
2476
+ else:
2477
+ raise ValueError("prior not in available_lens_prior_list_and_its_params['external_shear_sl']")
2478
+
2479
+ @property
2480
+ def density_profile_slope(self):
2481
+ return self._density_profile_slope
2482
+
2483
+ @density_profile_slope.setter
2484
+ def density_profile_slope(self, prior):
2485
+ if prior in self.available_lens_prior_list_and_its_params["density_profile_slope"]:
2486
+ print(f"using ler available density_profile_slope function : {prior}")
2487
+ args = self.lens_param_samplers_params["density_profile_slope"]
2488
+ if args is None:
2489
+ self._density_profile_slope = getattr(self, prior)(size=None, get_attribute=True)
2490
+ else:
2491
+ self._density_profile_slope = getattr(self, prior)(size=None, get_attribute=True, **args)
2492
+ elif callable(prior):
2493
+ print("using user provided custom density_profile_slope function")
2494
+ self._density_profile_slope = FunctionConditioning(function=None, x_array=None, create_rvs=prior)
2495
+ elif isinstance(prior, object):
2496
+ print("using user provided custom density_profile_slope class/object")
2497
+ self._density_profile_slope = prior
2498
+ else:
2499
+ raise ValueError("prior not in available_lens_prior_list_and_its_params['density_profile_slope']")
2500
+
2501
+ @property
2502
+ def density_profile_slope_sl(self):
2503
+ return self._density_profile_slope_sl
963
2504
 
2505
+ @density_profile_slope_sl.setter
2506
+ def density_profile_slope_sl(self, prior):
2507
+ if prior in self.available_lens_prior_list_and_its_params["density_profile_slope_sl"]:
2508
+ print(f"using ler available density_profile_slope_sl function : {prior}")
2509
+ args = self.lens_param_samplers_params["density_profile_slope_sl"]
2510
+ if args is None:
2511
+ self._density_profile_slope_sl = getattr(self, prior)(size=None, get_attribute=True)
2512
+ else:
2513
+ self._density_profile_slope_sl = getattr(self, prior)(size=None, get_attribute=True, **args)
2514
+ elif callable(prior):
2515
+ print("using user provided custom density_profile_slope_sl function")
2516
+ self._density_profile_slope_sl = FunctionConditioning(function=None, x_array=None, create_rvs=prior)
2517
+ elif isinstance(prior, object):
2518
+ print("using user provided custom density_profile_slope_sl class/object")
2519
+ self._density_profile_slope_sl = prior
2520
+ else:
2521
+ raise ValueError("prior not in available_lens_prior_list_and_its_params['density_profile_slope_sl']")
2522
+
2523
+
2524
+ ####################################
2525
+ # Available samplers and functions #
2526
+ ####################################
964
2527
  @property
965
- def available_axis_ratio_list_and_its_params(self):
2528
+ def available_lens_prior_list_and_its_params(self):
966
2529
  """
967
- Function to list all available axis ratio sampler.
2530
+ Dictionary with list all the available priors and it's corresponding parameters. This is an immutable instance attribute.
968
2531
  """
969
- self._available_axis_ratio_list_and_its_params = dict(
970
- axis_ratio_rayleigh=dict(q_min=0.2, q_max=1.0),
971
- axis_ratio_padilla_strauss=dict(q_min=0.2, q_max=1.0),
972
- axis_ratio_SIS=None,
2532
+
2533
+ self._available_lens_prior_list_and_its_params = dict(
2534
+ source_redshift_sl = dict(
2535
+ strongly_lensed_source_redshifts=None
2536
+ ),
2537
+ lens_redshift=dict(
2538
+ lens_redshift_SDSS_catalogue_sis=None,
2539
+ lens_redshift_SDSS_catalogue_numerical=None,
2540
+ lens_redshift_SDSS_catalogue_hemanta=None,
2541
+ ),
2542
+ velocity_dispersion = dict(
2543
+ velocity_dispersion_gengamma = dict(
2544
+ sigma_min=100., sigma_max=400., alpha=0.94, beta=1.85, phistar=2.099e-2*(self.cosmo.h/0.7)**3, sigmastar=113.78
2545
+ ),
2546
+ velocity_dispersion_choi = dict(
2547
+ sigma_min=100., sigma_max=400., alpha = 2.32, beta = 2.67, phistar = 8.0e-3*self.cosmo.h**3, sigmastar = 161.0
2548
+ ),
2549
+ velocity_dispersion_bernardi = dict(
2550
+ sigma_min=100., sigma_max=400., alpha=0.94, beta=1.85, phistar=2.099e-2*(self.cosmo.h/0.7)**3, sigmastar=113.78
2551
+ ),
2552
+ velocity_dispersion_ewoud = dict(
2553
+ sigma_min=100., sigma_max=400., alpha=0.94, beta=1.85, phistar=2.099e-2*(self.cosmo.h/0.7)**3, sigmastar=113.78
2554
+ ),
2555
+ ),
2556
+ axis_ratio = dict(
2557
+ axis_ratio_rayleigh = dict(q_min=0.2, q_max=1.0),
2558
+ axis_ratio_padilla_strauss = dict(q_min=0.2, q_max=1.0),
2559
+ axis_ratio_uniform = dict(q_min=0.2, q_max=1.0),
2560
+ ),
2561
+ axis_rotation_angle = dict(
2562
+ axis_rotation_angle_uniform = dict(
2563
+ phi_min=0.0, phi_max=2 * np.pi
2564
+ ),
2565
+ ),
2566
+ external_shear = dict(
2567
+ external_shear_normal = dict(mean=0., std=0.05),
2568
+ ),
2569
+ external_shear_sl = dict(
2570
+ external_shear_normal = dict(mean=0., std=0.05, name="external_shear_normal_sl"),
2571
+ external_shear_sl_numerical_hemanta = dict(external_shear_normal=dict(mean=0., std=0.05), name="external_shear_sl_numerical_hemanta"),
2572
+ ),
2573
+ density_profile_slope = dict(
2574
+ density_profile_slope_normal=dict(mean=1.99, std=0.149),
2575
+ ),
2576
+ density_profile_slope_sl = dict(
2577
+ density_profile_slope_normal=dict(mean=2.091, std=0.133, name="density_profile_slope_normal_sl"),
2578
+ density_profile_slope_sl_numerical_hemanta=dict(density_profile_slope_normal=dict(mean=1.99, std=0.149), name="density_profile_slope_sl_numerical_hemanta"),
2579
+ ),
2580
+ source_parameters=dict(
2581
+ sample_gw_parameters=None
2582
+ ),
973
2583
  )
974
2584
 
975
- return self._available_axis_ratio_list_and_its_params
2585
+ return self._available_lens_prior_list_and_its_params
976
2586
 
977
2587
  @property
978
- def available_optical_depth_list_and_its_params(self):
2588
+ def available_lens_functions_and_its_params(self):
979
2589
  """
980
- Function to list all available optical depth sampler.
2590
+ Dictionary with list all the available lens functions. This is an immutable instance attribute.
981
2591
  """
982
- self._available_optical_depth_list_and_its_params = dict(
983
- optical_depth_SIS_haris=None,
984
- optical_depth_SIS_hemanta=None,
985
- optical_depth_SIE_hemanta=None,
2592
+
2593
+ self._available_lens_functions_and_its_params = dict(
2594
+ strong_lensing_condition=dict(
2595
+ rjs_with_cross_section_sie_feixu=None,
2596
+ rjs_with_cross_section_sis=None,
2597
+ rjs_with_cross_section_mp=None,
2598
+ ),
2599
+ optical_depth=dict(
2600
+ optical_depth_sis_haris=None,
2601
+ optical_depth_sie_hemanta=None,
2602
+ optical_depth_epl_shear_hemanta=None,
2603
+ optical_depth_numerical=None,
2604
+ ),
2605
+ param_sampler_type=dict(
2606
+ sample_all_routine_sie_sl=None,
2607
+ sample_all_routine_sis_sl=None,
2608
+ sample_all_routine_epl_shear_sl=None,
2609
+ ),
2610
+ cross_section=dict(
2611
+ cross_section_sie_feixu=None,
2612
+ cross_section_sis=None,
2613
+ cross_section_caustic_area=None,
2614
+ interpolated_cross_section_function=None,
2615
+ ),
986
2616
  )
987
- return self._available_optical_depth_list_and_its_params
2617
+
2618
+ return self._available_lens_functions_and_its_params
2619
+
988
2620
 
989
2621
 
990
2622