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 +62 -0
- wolensing/__about__.py +1 -0
- wolensing/amplification_factor/.ipynb_checkpoints/amplification_factor-checkpoint.py +266 -0
- wolensing/amplification_factor/__init__.py +0 -0
- wolensing/amplification_factor/amplification_factor.py +305 -0
- wolensing/lensmodels/.ipynb_checkpoints/hessian-checkpoint.py +58 -0
- wolensing/lensmodels/.ipynb_checkpoints/lens-checkpoint.py +146 -0
- wolensing/lensmodels/__init__.py +0 -0
- wolensing/lensmodels/hessian.py +56 -0
- wolensing/lensmodels/lens.py +143 -0
- wolensing/lensmodels/potential.py +47 -0
- wolensing/plot/.ipynb_checkpoints/plot-checkpoint.py +50 -0
- wolensing/plot/__init__.py +0 -0
- wolensing/plot/plot.py +50 -0
- wolensing/utils/.ipynb_checkpoints/utils-checkpoint.py +154 -0
- wolensing/utils/__init__.py +0 -0
- wolensing/utils/histogram.py +123 -0
- wolensing/utils/utils.py +149 -0
- wolensing-0.0.1.dist-info/METADATA +60 -0
- wolensing-0.0.1.dist-info/RECORD +23 -0
- wolensing-0.0.1.dist-info/WHEEL +4 -0
- wolensing-0.0.1.dist-info/entry_points.txt +0 -0
- wolensing-0.0.1.dist-info/license_files/LICENSE +21 -0
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
|
+
|