wolensing 0.0.1__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.
docs/source/conf.py ADDED
@@ -0,0 +1,62 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ #
3
+ # This file only contains a selection of the most common options. For a full
4
+ # list see the documentation:
5
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html
6
+
7
+ # -- Path setup --------------------------------------------------------------
8
+
9
+ # If extensions (or modules to document with autodoc) are in another directory,
10
+ # add these directories to sys.path here. If the directory is relative to the
11
+ # documentation root, use os.path.abspath to make it absolute, like shown here.
12
+ #
13
+ import os
14
+ import sys
15
+ sys.path.insert(0, os.path.abspath('../../wolensing/'))
16
+
17
+
18
+ # -- Project information -----------------------------------------------------
19
+
20
+ project = 'wolensing'
21
+ copyright = '2023, smcu'
22
+ author = 'smcu'
23
+
24
+ # The full version, including alpha/beta/rc tags
25
+ release = '0.0.0'
26
+
27
+
28
+ # -- General configuration ---------------------------------------------------
29
+
30
+ # Add any Sphinx extension module names here, as strings. They can be
31
+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
32
+ # ones.
33
+ extensions = ['sphinx.ext.autodoc',
34
+ 'sphinx.ext.coverage',
35
+ 'sphinx.ext.viewcode'
36
+ ]
37
+
38
+ autodoc_default_options = {
39
+ "member-order": "bysource",
40
+ "special-members": "__init__",
41
+ }
42
+ # Add any paths that contain templates here, relative to this directory.
43
+ templates_path = ['_templates']
44
+
45
+ # List of patterns, relative to source directory, that match files and
46
+ # directories to ignore when looking for source files.
47
+ # This pattern also affects html_static_path and html_extra_path.
48
+ exclude_patterns = []
49
+
50
+ add_module_names = False
51
+
52
+ # -- Options for HTML output -------------------------------------------------
53
+
54
+ # The theme to use for HTML and HTML Help pages. See the documentation for
55
+ # a list of builtin themes.
56
+ #
57
+ html_theme = 'sphinx_rtd_theme'
58
+
59
+ # Add any paths that contain custom static files (such as style sheets) here,
60
+ # relative to this directory. They are copied after the builtin static files,
61
+ # so a file named "default.css" will overwrite the builtin "default.css".
62
+ html_static_path = ['_static']
wolensing/__about__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "2023.11.26.2123"
@@ -0,0 +1,266 @@
1
+ import numpy as np
2
+ from lenstronomy.LensModel.lens_model import LensModel
3
+ from scipy.optimize import curve_fit
4
+ from fast_histogram import histogram1d
5
+ from scipy.fft import fftfreq
6
+ from scipy.fftpack import fft
7
+ import lensinggw.constants.constants as const
8
+ from tqdm import trange, tqdm
9
+
10
+ from wolensing.utils.utils import *
11
+ from wolensing.utils.histogram import *
12
+ from wolensing.lensmodels.potential import potential
13
+
14
+ G = const.G # gravitational constant [m^3 kg^-1 s^-2]
15
+ c = const.c # speed of light [m/s]
16
+ M_sun = const.M_sun # Solar mass [Kg]
17
+
18
+ class amplification_factor(object):
19
+
20
+ def __init__(self, lens_model_list=None, kwargs_lens=None, kwargs_macro=None, **kwargs):
21
+ """
22
+
23
+ :param lens_model_list: list of lens models
24
+ :param kwargs_lens: arguments for integrating the diffraction integral
25
+ :param kwargs_macro: arguments of the macromodel
26
+ """
27
+
28
+ kwargs_integrator = {
29
+ 'TimeStep': 1e-5,
30
+ 'TimeMax': 100,
31
+ 'TimeMin': -50,
32
+ 'TimeLength': 10, # length in time considered after initial signal
33
+ 'TExtend': 10,
34
+ 'T0': 0,
35
+ 'Tscale': 0.,
36
+ 'WindowSize': 15,
37
+ 'PixelNum': 10000,
38
+ 'PixelBlockMax': 2000, # max number of pixels in a block
39
+ 'WindowCenterX': 0,
40
+ 'WindowCenterY': 0,
41
+ 'LastImageT': .02,
42
+ 'Tbuffer':0
43
+ }
44
+
45
+ for key in kwargs_integrator.keys():
46
+ if key in kwargs:
47
+ value = kwargs[key]
48
+ kwargs_integrator.update({key: value})
49
+
50
+ self._Tscale = kwargs_integrator['Tscale']
51
+ self._kwargs_lens = kwargs_lens
52
+ self._kwargs_macro = kwargs_macro
53
+ self._kwargs_integrator = kwargs_integrator
54
+ self._lens_model_list = lens_model_list
55
+ if lens_model_list != None:
56
+ self._lens_model_complete = LensModel(lens_model_list = lens_model_list)
57
+
58
+ def integrator(self, gpu=False):
59
+ """
60
+ Computes the amplification facator F(f) by constructing the histogram in time domain. Defines the integration window of lens plane first.
61
+
62
+ :param gpu: boolean, if True, use gpu computing for integration.
63
+ :return: amplification factor in time domain.
64
+ """
65
+
66
+
67
+ # details of the lens model and source
68
+ thetaE = self._kwargs_macro['theta_E']
69
+ y0 = self._kwargs_macro['source_pos_x']
70
+ y1 = self._kwargs_macro['source_pos_y']
71
+
72
+ # defines the time integration
73
+ binmax0 = self._kwargs_integrator['TimeMax']
74
+ binmin = self._kwargs_integrator['TimeMin']
75
+ binlength = self._kwargs_integrator['TimeLength']
76
+ binwidth = self._kwargs_integrator['TimeStep']
77
+
78
+ binnum = int((binmax0 - binmin) / binwidth) + 1
79
+ binnumlength = int(binlength / binwidth)
80
+ binmax = binmin + binwidth * (binnum + 1)
81
+ bins = np.linspace(binmin, binmax, binnum)
82
+
83
+ # dividing the lens plane into grid
84
+ N = self._kwargs_integrator['PixelNum']
85
+ Nblock = self._kwargs_integrator['PixelBlockMax']
86
+
87
+ x1cen = self._kwargs_integrator['WindowCenterX'] # The positions where the window centered at, usually the lens or the macroimage in embedded lens case
88
+ x2cen = self._kwargs_integrator['WindowCenterY']
89
+ L = 1. * self._kwargs_integrator['WindowSize'] # Size of the integration window
90
+ dx = L / (N - 1)
91
+
92
+ x1corn = x1cen - L / 2
93
+ x2corn = x2cen - L / 2
94
+ Lblock = Nblock * dx
95
+ Numblocks = N // Nblock
96
+ Nresidue = N % Nblock
97
+
98
+
99
+ if gpu:
100
+ bincount = histogram_routine_gpu(self._lens_model_list, Numblocks, np.array([[None, None]]), Nblock, Nresidue, x1corn, x2corn, Lblock, binnum,
101
+ binmin, binmax, thetaE, self._kwargs_lens, y0, y1, dx)
102
+ else:
103
+ bincount = histogram_routine_cpu(self._lens_model_complete, Numblocks, np.array([[None, None]]), Nblock, Nresidue, x1corn, x2corn, Lblock, binnum,
104
+ binmin, binmax, thetaE, self._kwargs_lens, y0, y1, dx)
105
+
106
+ # trimming the array
107
+ bincountback = np.trim_zeros(bincount, 'f')
108
+ bincountfront = np.trim_zeros(bincount, 'b')
109
+ fronttrimmed = len(bincount) - len(bincountback)
110
+ backtrimmed = len(bincount) - len(bincountfront) + 1
111
+ self._F_tilde = bincount[fronttrimmed:-backtrimmed] / (2 * np.pi * binwidth) / thetaE ** 2
112
+ self._ts = bins[fronttrimmed:-backtrimmed] - bins[fronttrimmed]
113
+ if binnumlength < len(self._ts):
114
+ self._ts, self._F_tilde = self._ts[:binnumlength], self._F_tilde[:binnumlength]
115
+ return self._ts, self._F_tilde
116
+
117
+ def fourier(self, freq_end=2000, type2=False):
118
+ """
119
+ Compute the amplification factor in frequency domain
120
+
121
+ :param freq_end: higher end of the frequency series. Default to be 2000.
122
+ :param type2: boolean, if True, switch to fourier transform of microlensing of a type 2 image.
123
+ :return: amplification factor in frequency domain.
124
+ """
125
+
126
+ if type2:
127
+ ws, Fw = iwFourier(self._ts * self._Tscale, self._F_tilde, type2)
128
+ fs = ws/(2*np.pi)
129
+ peak = np.where(self._F_tilde == np.amax(self._F_tilde))
130
+ index = int(peak[0])
131
+ Tds = 5 # in dimension time
132
+ tdiff = self._ts[index]*self._Tscale-5
133
+ # tdiff = ts[index]*self._Tscale-Tds
134
+ overall_phase = np.exp(-1 * 2 * np.pi * 1j * (Tds+tdiff) * fs)
135
+ Fw *= overall_phase
136
+ else:
137
+ ts_extended, F_tilde_extended = F_tilde_extend(self._ts, self._F_tilde, self._kwargs_integrator)
138
+ ws, Fw = iwFourier(ts_extended*self._Tscale, F_tilde_extended)
139
+
140
+ from bisect import bisect_left
141
+ i = bisect_left(ws, 2*np.pi*2000)
142
+
143
+ self._ws, self._Fws = ws, Fw
144
+ return ws[:i], Fw[:i]
145
+
146
+ def importor(self, ts=None, F_tilde=None, ws=None, Fws=None, time=False, freq=False):
147
+ """
148
+ Imports the amplification factor
149
+
150
+ :param ts: time series in dimensionless unit
151
+ :param F_tilde: time domain amplification factor
152
+ :param ws: sampling frequency in unit of angular frequency
153
+ :param Fws: frequency domain amplification factor
154
+ :param time: boolean, if True, plot time domain amplification factor
155
+ :param freq: boolean, if True, plot frequency domain amplification factor
156
+ """
157
+ if time:
158
+ self._ts = ts
159
+ self._F_tilde = F_tilde
160
+ elif freq:
161
+ self._ws = ws
162
+ self._Fws = Fws
163
+ else:
164
+ raise Exception('Please choose either time domain or frequency domain to import.')
165
+
166
+ def plot_time(self, saveplot=None):
167
+ """
168
+ Plots the amplification factor in time domain
169
+
170
+ :param saveplot: where the plot is saved.
171
+ :return: axes class of matplotlib representing the plot.
172
+ """
173
+
174
+ try:
175
+ self._ts
176
+ except NameError:
177
+ raise Exception('Time data is empty. Either integrate or import time data.')
178
+
179
+ import matplotlib.pyplot as plt
180
+ plt.clf()
181
+ plt.rc('text', usetex=True)
182
+ plt.rc('font', family='serif')
183
+ fig, ax = plt.subplots()
184
+
185
+ ts = self._ts
186
+ F_tilde = self._F_tilde
187
+
188
+ # smoothen the curve(s)
189
+ from scipy.signal import savgol_filter
190
+ F_smooth = savgol_filter(F_tilde, 51, 3)
191
+
192
+ ax.plot(ts, F_smooth, linewidth=1)
193
+
194
+ ax.set_xlabel(r'Time (1s/Tscale)', fontsize = 14)
195
+ ax.set_ylabel(r'$F(t)$', fontsize = 14)
196
+ ax.tick_params(axis='x', labelsize=11)
197
+ ax.tick_params(axis='y', labelsize=11)
198
+ ax.grid(which = 'both', alpha = 0.5)
199
+ fig.tight_layout()
200
+
201
+ if saveplot != None:
202
+ plt.savefig(saveplot)
203
+
204
+ plt.show()
205
+ return ax
206
+
207
+ def plot_freq(self, freq_end = 2000, saveplot=None, abs=True, pha=False):
208
+ """
209
+ Plots the amplification factor against frequency in semilogx
210
+
211
+ :param freq_end: higher end of the frequency range
212
+ :param abs: boolean, if True, compute the absolute value of the amplification.
213
+ :param pha: boolean, if True, compute the phase of the amplification.
214
+ :param saveplot: where the plot is saved.
215
+ :return: axes class of matplotlib representing the plot.
216
+ """
217
+
218
+ try:
219
+ self._ws
220
+ except NameError:
221
+ raise Exception('Frequency data is empty. Either integrate or import frequency data.')
222
+
223
+
224
+ # Either plot the absolute value or the argument
225
+ if pha:
226
+ abs=False
227
+
228
+ import matplotlib.pyplot as plt
229
+ plt.clf()
230
+ plt.rc('text', usetex=True)
231
+ plt.rc('font', family='serif')
232
+ fig, ax = plt.subplots()
233
+
234
+ ws = self._ws
235
+ Fws = self._Fws
236
+
237
+ fs=ws/(2*np.pi)
238
+
239
+ # smoothen the curve(s)
240
+ from scipy.signal import savgol_filter
241
+ Fa_fil = savgol_filter(np.abs(Fws), 51, 3)
242
+ Fp_fil = savgol_filter(np.angle(Fws), 51, 3)
243
+
244
+ from bisect import bisect_left
245
+ i = bisect_left(fs, freq_end)
246
+
247
+ if abs:
248
+ ax.semilogx(fs[:i], Fa_fil[:i], linewidth=1)
249
+ elif pha:
250
+ ax.semilogx(fs[:i], Fp_fil[:i], linewidth=1)
251
+
252
+ ax.set_xlabel(r'Frequency (Hz)', fontsize = 14)
253
+ if abs:
254
+ ax.set_ylabel(r'$|F|/\sqrt{\mu}$', fontsize = 14)
255
+ elif pha:
256
+ ax.set_ylabel(r'$args(F)$', fontsize = 14)
257
+ ax.tick_params(axis='x', labelsize=11)
258
+ ax.tick_params(axis='y', labelsize=11)
259
+ ax.grid(which = 'both', alpha = 0.5)
260
+ fig.tight_layout()
261
+
262
+ if saveplot != None:
263
+ plt.savefig(saveplot)
264
+
265
+ plt.show()
266
+ return ax
File without changes
@@ -0,0 +1,305 @@
1
+ import numpy as np
2
+ from lenstronomy.LensModel.lens_model import LensModel
3
+ from scipy.optimize import curve_fit
4
+ from fast_histogram import histogram1d
5
+ from scipy.fft import fftfreq
6
+ from scipy.fftpack import fft
7
+ import lensinggw.constants.constants as const
8
+ from tqdm import trange, tqdm
9
+
10
+ from wolensing.utils.utils import *
11
+ from wolensing.utils.histogram import *
12
+ from wolensing.lensmodels.potential import potential
13
+
14
+ G = const.G # gravitational constant [m^3 kg^-1 s^-2]
15
+ c = const.c # speed of light [m/s]
16
+ M_sun = const.M_sun # Solar mass [Kg]
17
+
18
+ class amplification_factor(object):
19
+
20
+ def __init__(self, lens_model_list=None, kwargs_lens=None, kwargs_macro=None, **kwargs):
21
+ """
22
+
23
+ :param lens_model_list: list of lens models
24
+ :param kwargs_lens: arguments for integrating the diffraction integral
25
+ :param kwargs_macro: arguments of the macromodel
26
+ """
27
+
28
+ kwargs_integrator = {
29
+ 'TimeStep': 1e-5,
30
+ 'TimeMax': 100,
31
+ 'TimeMin': -50,
32
+ 'TimeLength': 10, # length in time considered after initial signal
33
+ 'TExtend': 10,
34
+ 'T0': 0,
35
+ 'Tscale': 0.,
36
+ 'WindowSize': 15,
37
+ 'PixelNum': 10000,
38
+ 'PixelBlockMax': 2000, # max number of pixels in a block
39
+ 'WindowCenterX': 0,
40
+ 'WindowCenterY': 0,
41
+ 'LastImageT': .02,
42
+ 'Tbuffer':0
43
+ }
44
+
45
+ for key in kwargs_integrator.keys():
46
+ if key in kwargs:
47
+ value = kwargs[key]
48
+ kwargs_integrator.update({key: value})
49
+
50
+ self._Tscale = kwargs_integrator['Tscale']
51
+ self._kwargs_lens = kwargs_lens
52
+ self._kwargs_macro = kwargs_macro
53
+ self._kwargs_integrator = kwargs_integrator
54
+ self._lens_model_list = lens_model_list
55
+ if lens_model_list != None:
56
+ self._lens_model_complete = LensModel(lens_model_list = lens_model_list)
57
+
58
+ def integrator(self, gpu=False):
59
+ """
60
+ Computes the amplification facator F(f) by constructing the histogram in time domain. Defines the integration window of lens plane first.
61
+
62
+ :param gpu: boolean, if True, use gpu computing for integration.
63
+ :return: amplification factor in time domain.
64
+ """
65
+
66
+ # details of the lens model and source
67
+ thetaE = self._kwargs_macro['theta_E']
68
+ y0 = self._kwargs_macro['source_pos_x']
69
+ y1 = self._kwargs_macro['source_pos_y']
70
+
71
+ # defines the time integration
72
+ binmax0 = self._kwargs_integrator['TimeMax']
73
+ binmin = self._kwargs_integrator['TimeMin']
74
+ binlength = self._kwargs_integrator['TimeLength']
75
+ binwidth = self._kwargs_integrator['TimeStep']
76
+
77
+ binnum = int((binmax0 - binmin) / binwidth) + 1
78
+ binnumlength = int(binlength / binwidth)
79
+ binmax = binmin + binwidth * (binnum + 1)
80
+ bins = np.linspace(binmin, binmax, binnum)
81
+
82
+ # dividing the lens plane into grid
83
+ N = self._kwargs_integrator['PixelNum']
84
+ Nblock = self._kwargs_integrator['PixelBlockMax']
85
+
86
+ x1cen = self._kwargs_integrator['WindowCenterX'] # The positions where the window centered at, usually the lens or the macroimage in embedded lens case
87
+ x2cen = self._kwargs_integrator['WindowCenterY']
88
+ L = 1. * self._kwargs_integrator['WindowSize'] # Size of the integration window
89
+ dx = L / (N - 1)
90
+
91
+ x1corn = x1cen - L / 2
92
+ x2corn = x2cen - L / 2
93
+ Lblock = Nblock * dx
94
+ Numblocks = N // Nblock
95
+ Nresidue = N % Nblock
96
+
97
+ if gpu:
98
+ bincount = histogram_routine_gpu(self._lens_model_list, Numblocks, np.array([[None, None]]), Nblock, Nresidue, x1corn, x2corn, Lblock, binnum,
99
+ binmin, binmax, thetaE, self._kwargs_lens, y0, y1, dx)
100
+ else:
101
+ bincount = histogram_routine_cpu(self._lens_model_complete, Numblocks, np.array([[None, None]]), Nblock, Nresidue, x1corn, x2corn, Lblock, binnum,
102
+ binmin, binmax, thetaE, self._kwargs_lens, y0, y1, dx)
103
+
104
+
105
+
106
+ # trimming the array
107
+ bincountback = np.trim_zeros(bincount, 'f')
108
+ bincountfront = np.trim_zeros(bincount, 'b')
109
+ fronttrimmed = len(bincount) - len(bincountback)
110
+ backtrimmed = len(bincount) - len(bincountfront) + 1
111
+ self._F_tilde = bincount[fronttrimmed:-backtrimmed] / (2 * np.pi * binwidth) / thetaE ** 2
112
+ self._ts = bins[fronttrimmed:-backtrimmed] - bins[fronttrimmed]
113
+ if binnumlength < len(self._ts):
114
+ self._ts, self._F_tilde = self._ts[:binnumlength], self._F_tilde[:binnumlength]
115
+ return self._ts, self._F_tilde
116
+
117
+ def fourier(self, freq_end=2000, type2=False):
118
+ """
119
+ Compute the amplification factor in frequency domain
120
+
121
+ :param freq_end: higher end of the frequency series. Default to be 2000.
122
+ :param type2: boolean, if True, switch to fourier transform of microlensing of a type 2 image.
123
+ :return: frequency array and amplification factor F(f) of wave optics.
124
+ """
125
+ dt = self._kwargs_integrator['TimeStep']*self._Tscale # precise timestep for fourier transform
126
+
127
+ if type2:
128
+ ws, Fw = iwFourier(self._ts * self._Tscale, self._F_tilde, dt)
129
+ fs = ws/(2*np.pi)
130
+ peak = np.where(self._F_tilde == np.amax(self._F_tilde))
131
+ index = int(peak[0])
132
+ Tds = (self._kwargs_integrator['T0'] - self._kwargs_integrator['TimeMin']) * self._Tscale # in dimension time
133
+ tdiff = self._ts[index]*self._Tscale - Tds
134
+ overall_phase = np.exp(-1 * 2 * np.pi * 1j * (Tds+tdiff) * fs)
135
+ Fw *= overall_phase
136
+ else:
137
+ ts_extended, F_tilde_extended = F_tilde_extend(self._ts, self._F_tilde, self._kwargs_macro, self._kwargs_integrator)
138
+ F_tilde_apodized = coswindowback(F_tilde_extended, 50)
139
+ ws, Fw = iwFourier(ts_extended*self._Tscale, F_tilde_apodized, dt)
140
+
141
+ from bisect import bisect_left
142
+ i = bisect_left(ws, 2*np.pi*freq_end)
143
+
144
+ self._fs, self._Fws = ws/(2*np.pi), Fw
145
+ return self._fs[:i], self._Fws[:i]
146
+
147
+ def importor(self, ts=None, F_tilde=None, fs=None, Fws=None, time=False, freq=False):
148
+ """
149
+ Imports the amplification factor
150
+
151
+ :param ts: time series in dimensionless unit
152
+ :param F_tilde: time domain amplification factor
153
+ :param ws: sampling frequency in unit of angular frequency
154
+ :param Fws: frequency domain amplification factor
155
+ :param time: boolean, if True, plot time domain amplification factor
156
+ :param freq: boolean, if True, plot frequency domain amplification factor
157
+ """
158
+ if time:
159
+ self._ts = ts
160
+ self._F_tilde = F_tilde
161
+ elif freq:
162
+ self._fs = fs
163
+ self._Fws = Fws
164
+ else:
165
+ raise Exception('Please choose either time domain or frequency domain to import.')
166
+
167
+ def plot_time(self, saveplot=None):
168
+ """
169
+ Plots the amplification factor in time domain
170
+
171
+ :param saveplot: where the plot is saved.
172
+ :return: axes class of matplotlib representing the plot.
173
+ """
174
+
175
+ try:
176
+ self._ts
177
+ except NameError:
178
+ raise Exception('Time data is empty. Either integrate or import time data.')
179
+
180
+ import matplotlib.pyplot as plt
181
+ plt.clf()
182
+ plt.rc('text', usetex=True)
183
+ plt.rc('font', family='serif')
184
+ fig, ax = plt.subplots()
185
+
186
+ ts = self._ts
187
+ F_tilde = self._F_tilde
188
+
189
+ # smoothen the curve(s)
190
+ from scipy.signal import savgol_filter
191
+ F_smooth = savgol_filter(F_tilde, 51, 3)
192
+
193
+ ax.plot(ts, F_smooth, linewidth=1)
194
+
195
+ ax.set_xlabel(r'Time (1s/Tscale)', fontsize = 14)
196
+ ax.set_ylabel(r'$F(t)$', fontsize = 14)
197
+ ax.tick_params(axis='x', labelsize=11)
198
+ ax.tick_params(axis='y', labelsize=11)
199
+ ax.grid(which = 'both', alpha = 0.5)
200
+ fig.tight_layout()
201
+
202
+ if saveplot != None:
203
+ plt.savefig(saveplot)
204
+
205
+ plt.show()
206
+ return ax
207
+
208
+ def plot_freq(self, macromu = 1, freq_end = 2000, saveplot=None, abs=True, pha=False, smooth=True):
209
+ """
210
+ Plots the amplification factor against frequency in semilogx
211
+
212
+ :param macromu: macro magnification of the strong lensed image. Default to be one.
213
+ :param freq_end: higher end of the frequency range
214
+ :param abs: boolean, if True, compute the absolute value of the amplification.
215
+ :param pha: boolean, if True, compute the phase of the amplification.
216
+ :param saveplot: where the plot is saved.
217
+ :return: axes class of matplotlib representing the plot.
218
+ """
219
+
220
+ try:
221
+ self._fs
222
+ except NameError:
223
+ raise Exception('Frequency data is empty. Either integrate or import frequency data.')
224
+
225
+
226
+ # Either plot the absolute value or the argument
227
+ if pha:
228
+ abs=False
229
+
230
+ import matplotlib.pyplot as plt
231
+ plt.clf()
232
+ plt.rc('text', usetex=True)
233
+ plt.rc('font', family='serif')
234
+ fig, ax = plt.subplots()
235
+
236
+ fs = self._fs
237
+ Fws = self._Fws
238
+
239
+ from bisect import bisect_left
240
+ i = bisect_left(fs, freq_end)
241
+
242
+ # smoothen the curve(s)
243
+ if smooth:
244
+ from scipy.signal import savgol_filter
245
+ Fa_fil = savgol_filter(np.abs(Fws), 51, 3)
246
+ Fp_fil = savgol_filter(np.angle(Fws), 51, 3)
247
+ if abs:
248
+ ax.semilogx(fs[:i], Fa_fil[:i], linewidth=1)
249
+ elif pha:
250
+ ax.semilogx(fs[:i], Fp_fil[:i], linewidth=1)
251
+
252
+ else:
253
+ if abs:
254
+ ax.semilogx(fs[:i], np.abs(Fws[:i]), linewidth=1)
255
+ elif pha:
256
+ ax.semilogx(fs[:i], np.angle(Fws[:i]), linewidth=1)
257
+
258
+ ax.set_xlabel(r'Frequency (Hz)', fontsize = 14)
259
+ if abs:
260
+ ax.set_ylabel(r'$|F|/\sqrt{\mu}$', fontsize = 14)
261
+ elif pha:
262
+ ax.set_ylabel(r'$args(F)$', fontsize = 14)
263
+ ax.tick_params(axis='x', labelsize=11)
264
+ ax.tick_params(axis='y', labelsize=11)
265
+ ax.grid(which = 'both', alpha = 0.5)
266
+ fig.tight_layout()
267
+
268
+ if saveplot != None:
269
+ plt.savefig(saveplot)
270
+
271
+ plt.show()
272
+ return ax
273
+
274
+ def geometrical_optics(self, mus, tds, Img_ra, Img_dec, upper_lim = 3000):
275
+ """
276
+ :param mus: magnifications of images.
277
+ :param tds: time delays of images.
278
+ :param Img_ra: right ascension of images relative to the center of lens plane.
279
+ :param Img_dec: declination of images relative to the center of lens plane.
280
+ :param upper_lim: desired upper limit of freqeuncy range of geometrical optics.
281
+ :return: frequency array and amplification factor F(f) of geometrical optics.
282
+ """
283
+ fs = self._fs
284
+ fs_grid = fs[1]-fs[0]
285
+
286
+ num_interp = int((upper_lim-fs[0])/fs_grid)
287
+ self._geofs = np.linspace(fs[0], upper_lim, num_interp)
288
+
289
+ ns = Morse_indices(self._lens_model_list, Img_ra, Img_dec, self._kwargs_lens)
290
+ from lensinggw.amplification_factor.amplification_factor import amplification_from_data
291
+ self._geoFws = amplification_from_data(self._geofs, mus, tds, ns)
292
+
293
+ return self._geofs, self._geoFws
294
+
295
+ def concatenate(self, transfreq = 1000):
296
+ """
297
+ :param transfreq: transitional frequency of wave optics to geometrical optics.
298
+ :return: concatenated frequency array and amplification factor F(f).
299
+ """
300
+ index = (np.abs(self._fs-transfreq)).argmin()
301
+
302
+ self._fullfs = np.concatenate((self._fs[:index], self._geofs[index:]))
303
+ self._fullFws = np.concatenate((self._Fws[:index], self._geoFws[index:]))
304
+
305
+ return self._fullfs, self._fullFws
@@ -0,0 +1,58 @@
1
+ import numpy as np
2
+ import warnings
3
+
4
+ def Hessian_Td(lens_model_list, x, y, kwargs):
5
+ '''
6
+ :param lens_model_list: list of lens models.
7
+ :param x: x-coordinates of position on lens plane.
8
+ :param y: y-coordinates of position on lens plane.
9
+ :kwargs: arguemnts for the lens models.
10
+ :return: independent components of hessian matrix of time delay function.
11
+ '''
12
+
13
+ hessian = np.array([1.,1.,0.])
14
+
15
+ for lens_type, lens_kwargs in zip(lens_model_list, kwargs):
16
+ thetaE = lens_kwargs['theta_E']
17
+ x_center = lens_kwargs['center_x']
18
+ y_center = lens_kwargs['center_y']
19
+
20
+ x_shift, y_shift = x-x_center, y-y_center
21
+
22
+ if lens_type == 'SIS':
23
+ hessian -= Hessian_SIS(x_shift, y_shift, thetaE)
24
+ elif lens_type == 'POINT_MASS':
25
+ hessian -= Hessian_PM(x_shift, y_shift, thetaE) # Make sure Psi_PM is JAX-compatible
26
+
27
+ print(hessian)
28
+
29
+ return hessian
30
+
31
+ def Hessian_SIS(x, y, thetaE):
32
+ '''
33
+ :param x: x-coordinates of position on lens plane with respect to the lens position.
34
+ :param y: y-coordinates of position on lens plane with respect to the lens position.
35
+ :param thetaE: Einstein radius of the lens.
36
+ :return: independent components of hessian matrix of SIS profile.
37
+ '''
38
+
39
+ prefactor = thetaE * np.sqrt(x**2 + y**2)**(-3.)
40
+ f_xx = y**2 * prefactor
41
+ f_yy = x**2 * prefactor
42
+ f_xy = -x * y * prefactor
43
+ return f_xx, f_yy, f_xy
44
+
45
+ def Hessian_PM(x, y, thetaE):
46
+ '''
47
+ :param x: x-coordinates of position on lens plane with respect to the lens position.
48
+ :param y: y-coordinates of position on lens plane with respect to the lens position.
49
+ :param thetaE: Einstein radius of the lens.
50
+ :return: independent components of hessian matrix of PM profile.
51
+ '''
52
+
53
+ prefactor = thetaE**2 * (x**2 + y**2)**(-2.)
54
+ f_xx = (-x**2 + y**2) * prefactor
55
+ f_yy = -1 * f_xx
56
+ f_xy = (-2 * x * y) * prefactor
57
+ return f_xx, f_yy, f_xy
58
+