rwlenspy 1.1.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
rwlenspy-1.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Zarif Kader
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.1
2
+ Name: rwlenspy
3
+ Version: 1.1.1
4
+ Summary: Lensing simulation from Fermat Potenials
5
+ Author: Zarif Kader
6
+ Author-email: kader.zarif@gmail.com
7
+ Requires-Python: >=3.9
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: Cython (>=0.29.33)
14
+ Requires-Dist: astropy (>=6.0.0)
15
+ Requires-Dist: matplotlib (>=3.8.2)
16
+ Requires-Dist: numpy (>=1.21.0)
17
+ Requires-Dist: scipy (>=1.7.0)
18
+ Requires-Dist: setuptools (>=66.1.1)
19
+ Description-Content-Type: text/markdown
20
+
21
+ # RWLensPy
22
+
23
+ This is a python package that generates observed morphologies and propagation transfer functions for radio wave propgation recorded by a radio telescope.
24
+
25
+ The code can be installed with:
26
+
27
+ `pip install rwlenspy`
28
+
29
+ ## Examples
30
+
31
+ For examples see `examples/`. The image ray trace is shown in the `example_animate_*.py` files and how to get the coherent transfer function for a baseband simulation is shown in `example_transfer*.py`.
32
+
33
+ <img src="./examples/plots/singelens_spatial_freqslice.gif" width=42%> <img src="./examples/plots/singlelens_baseband_spatial_arrival.gif" width=42%>
34
+
35
+ ## Custom/Dev Install
36
+
37
+ The package is built with Poetry and Cython using C++11 and OpenMP. This requires having a compiler like `gcc` if one is editing the code. If one requires a dev install, this can be done with:
38
+
39
+ `poetry install --with test,dev`
40
+
41
+ `poetry run python`
42
+
43
+ Once installed, tests can be run with:
44
+
45
+ `poetry run pytest`
46
+
@@ -0,0 +1,25 @@
1
+ # RWLensPy
2
+
3
+ This is a python package that generates observed morphologies and propagation transfer functions for radio wave propgation recorded by a radio telescope.
4
+
5
+ The code can be installed with:
6
+
7
+ `pip install rwlenspy`
8
+
9
+ ## Examples
10
+
11
+ For examples see `examples/`. The image ray trace is shown in the `example_animate_*.py` files and how to get the coherent transfer function for a baseband simulation is shown in `example_transfer*.py`.
12
+
13
+ <img src="./examples/plots/singelens_spatial_freqslice.gif" width=42%> <img src="./examples/plots/singlelens_baseband_spatial_arrival.gif" width=42%>
14
+
15
+ ## Custom/Dev Install
16
+
17
+ The package is built with Poetry and Cython using C++11 and OpenMP. This requires having a compiler like `gcc` if one is editing the code. If one requires a dev install, this can be done with:
18
+
19
+ `poetry install --with test,dev`
20
+
21
+ `poetry run python`
22
+
23
+ Once installed, tests can be run with:
24
+
25
+ `poetry run pytest`
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ import numpy
4
+ from setuptools.command.build_ext import build_ext
5
+ from setuptools import Extension
6
+
7
+ from pathlib import Path
8
+
9
+ root = Path(__file__).parent
10
+
11
+ print("Build File Imported")
12
+
13
+ # See if Cython is installed
14
+ try:
15
+ from Cython.Build import cythonize
16
+ # If Cython is not available
17
+ except ImportError:
18
+ def build(setup_kwargs):
19
+ print("Build without Cython")
20
+ ext_modules = [
21
+ Extension(
22
+ "rwlenspy.lensing",
23
+ ["rwlenspy/lensing.cpp"],
24
+ )
25
+ ]
26
+ setup_kwargs.update(
27
+ {
28
+ "ext_modules": ext_modules,
29
+ "include_dirs": [numpy.get_include()],
30
+ }
31
+ )
32
+ # Cython is installed, Compile.
33
+ else:
34
+ # This function will be executed in setup.py:
35
+ def build(setup_kwargs):
36
+ print("Build with Cython")
37
+ # The files you want to compile
38
+ ext_modules = [
39
+ Extension(
40
+ "rwlenspy.lensing",
41
+ sources=["rwlenspy/lensing.pyx", "rwlenspy/rwlens.cpp"],
42
+ include_dirs=[numpy.get_include()],
43
+ extra_compile_args=["-fopenmp", "-std=c++11"],
44
+ extra_link_args=["-fopenmp"],
45
+ define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")],
46
+ language="c++"
47
+ )
48
+ ]
49
+
50
+ # Build
51
+ setup_kwargs.update(
52
+ {
53
+ "ext_modules": cythonize(
54
+ ext_modules,
55
+ language_level=3,
56
+ compiler_directives={"linetrace": True},
57
+ ),
58
+ "cmdclass": {"build_ext": build_ext},
59
+ }
60
+ )
@@ -0,0 +1,368 @@
1
+ from pathlib import Path
2
+ from time import time
3
+
4
+ import matplotlib as mpl
5
+ import matplotlib.animation
6
+ import matplotlib.cm as cmaps
7
+ import matplotlib.pyplot as plt
8
+ import numpy as np
9
+ from astropy import constants as c
10
+ from astropy import cosmology
11
+ from astropy import units as u
12
+ from matplotlib.colors import LogNorm
13
+ from scipy.fft import rfftfreq
14
+
15
+ import rwlenspy.lensing as rwl
16
+ from rwlenspy.utils import LogLens, RandomGaussianLens
17
+
18
+ # Matplotlib setup
19
+ GREYMAP = mpl.cm.__dict__["Greys"]
20
+ mpl.rcParams["figure.figsize"] = [8.0, 6.0]
21
+ mpl.rcParams["figure.dpi"] = 80
22
+ mpl.rcParams["savefig.dpi"] = 100
23
+ mpl.rcParams["font.size"] = 12
24
+ mpl.rcParams["legend.fontsize"] = "large"
25
+ mpl.rcParams["figure.titlesize"] = "large"
26
+ mpl.rcParams["agg.path.chunksize"] = 10000
27
+ plt.rcParams["savefig.dpi"] = 70
28
+
29
+ """
30
+ ############################
31
+ #### Lensing Ray Trace #####
32
+ ############################
33
+ """
34
+
35
+ """
36
+ Diagram of Lensing System setup.
37
+ ############################################################
38
+ # | | | |
39
+ # | | | |
40
+ # | | | |
41
+ # | | | |
42
+ # obs r1 r2 src
43
+ ############################################################
44
+ """
45
+ # Memory in bytes
46
+ max_memory = 4e9
47
+
48
+ cosmo = cosmology.Planck18
49
+
50
+ # Comoving
51
+ D_obs_src = 1 * u.Gpc
52
+ D_obs_r1 = D_obs_src / 2
53
+ D_obs_r2 = 3 * D_obs_src / 4
54
+
55
+ # redshift
56
+ z_obs_r1 = cosmology.z_at_value(cosmo.comoving_distance, D_obs_r1)
57
+ z_obs_r2 = cosmology.z_at_value(cosmo.comoving_distance, D_obs_r2)
58
+ z_obs_src = cosmology.z_at_value(cosmo.comoving_distance, D_obs_src)
59
+
60
+ # Ang. Diam. Distance
61
+ D_obs_r1 = cosmo.angular_diameter_distance(z_obs_r1)
62
+ D_obs_r2 = cosmo.angular_diameter_distance(z_obs_r2)
63
+ D_r1_r2 = cosmo.angular_diameter_distance_z1z2(z_obs_r1, z_obs_r2)
64
+ D_obs_src = cosmo.angular_diameter_distance(z_obs_src)
65
+ D_r2_src = cosmo.angular_diameter_distance_z1z2(z_obs_r2, z_obs_src)
66
+
67
+ # Physical Lens (r2) Params
68
+ r_e = c.alpha**2 * c.a0 # classical electron radius
69
+ kdm = (
70
+ (r_e * c.c / (2 * np.pi)).to(u.cm**2 / u.s)
71
+ * ((1.0 * u.pc / u.cm).to(u.m / u.m)).value
72
+ ).value
73
+ const_Dr2 = D_r2_src / (D_obs_r2 * D_obs_src)
74
+ lens_r2_scale = (5000 * u.AU / D_obs_r2).to(u.m / u.m)
75
+ scale_r2 = lens_r2_scale.value
76
+ sig_DM = 0.0005
77
+ geom_const_r2 = ((1 / (const_Dr2 * c.c)).to(u.s)).value
78
+ geom_const_r2 = geom_const_r2 * scale_r2**2
79
+ lens_const_r2 = kdm * sig_DM
80
+ freq_power_r2 = -2.0
81
+ beta_r2_x = 0.0
82
+ beta_r2_y = 0.0
83
+
84
+ # Physical Lens (r1) Params
85
+ Eins_time_const = 4 * c.G * c.M_sun / c.c**3
86
+ const_Dr1 = D_r1_r2 / (D_obs_r1 * D_obs_r2)
87
+ mass = 1 # solar mass
88
+ lens_r1_scale = np.sqrt(mass * Eins_time_const * c.c * const_Dr1).to(u.m / u.m)
89
+ scale_r1 = lens_r1_scale.value
90
+ geom_const_r1 = ((1 / (const_Dr1 * c.c)).to(u.s)).value
91
+ geom_const_r1 = geom_const_r1 * scale_r1**2
92
+ lens_const_r1 = mass * Eins_time_const.to(u.s).value
93
+ freq_power_r1 = 0
94
+ beta_r1_x = 1.5
95
+ beta_r1_y = 0.0
96
+
97
+ # Sim Parameters
98
+ freq_ref = 800e6
99
+ freqs = 800e6 - rfftfreq(2048, d=1 / (800e6)) # MHz
100
+ nyqalias = True
101
+
102
+ # Grid Parameters
103
+ max_fres = 5
104
+ theta_min = -max_fres
105
+ theta_max = max_fres
106
+ theta_N = 251
107
+
108
+ # Spatial Grid
109
+ x1 = np.arange(theta_N) * (theta_max - theta_min) / (theta_N - 1) + theta_min
110
+
111
+ # Lens functions
112
+ seed = 4321
113
+ lens_arr_r2 = RandomGaussianLens(theta_N, theta_N, 1, seed=seed)
114
+ lens_arr_r1 = -1.0 * LogLens(x1[:, None], x1[None, :])
115
+
116
+ lens_arr_r2 = lens_arr_r2.astype(np.double).ravel(order="C")
117
+ lens_arr_r1 = lens_arr_r1.astype(np.double).ravel(order="C")
118
+ freqs = freqs.astype(np.double).ravel(order="C")
119
+
120
+ # Get Images
121
+ print("Getting the Images")
122
+ t1 = time()
123
+ txvals, tyvals, fvals, delayvals, magvals = rwl.GetMultiplaneFreqStationaryPoints(
124
+ theta_min,
125
+ theta_max,
126
+ theta_N,
127
+ freqs,
128
+ freq_ref,
129
+ lens_arr_r2,
130
+ scale_r2,
131
+ beta_r2_x,
132
+ beta_r2_y,
133
+ geom_const_r2,
134
+ lens_const_r2,
135
+ freq_power_r2,
136
+ lens_arr_r1,
137
+ scale_r1,
138
+ beta_r1_x,
139
+ beta_r1_y,
140
+ geom_const_r1,
141
+ lens_const_r1,
142
+ freq_power_r1,
143
+ max_memory
144
+ )
145
+ tv = time() - t1
146
+ print("Total Time :", tv, "s", " | ", tv / 60, "min", tv / 3600, "hr")
147
+
148
+ txvals = np.asarray(txvals)
149
+ tyvals = np.asarray(tyvals)
150
+ fvals = np.asarray(fvals)
151
+ delayvals = np.asarray(delayvals)
152
+ magvals = np.asarray(magvals)
153
+
154
+ """
155
+ ##########################
156
+ #### Animate Spatial #####
157
+ ##########################
158
+ """
159
+ # Spatial Animation setup
160
+ num_frames = freqs.size
161
+
162
+ # Setup plot
163
+ fig = plt.figure()
164
+ ax = fig.add_subplot(111)
165
+ axsc = ax.scatter([], [], s=4)
166
+ axisscale = 1e-6
167
+ axisstr = "milli"
168
+ framescale = 1e6
169
+ framestr = "M"
170
+ scaling = scale_r1 * 206264.806247 / axisscale
171
+
172
+
173
+ # Select largest spatial extent
174
+ cut1 = fvals == np.amin(fvals) # lowest freq for scattering
175
+ maxv_ = (
176
+ max(np.amax(np.abs(txvals[cut1])), np.amax(np.abs(tyvals[cut1]))) * scaling * 1.1
177
+ )
178
+
179
+ # Set axes and plot
180
+ ax.set_ylim(-maxv_, maxv_)
181
+ ax.set_xlim(-maxv_, maxv_)
182
+ ax.set_ylabel(f"$\\theta_Y$ [{axisstr}arcsec]", size=14)
183
+ ax.set_xlabel(f"$\\theta_X$ [{axisstr}arcsec]", size=14)
184
+ ax.set_facecolor("black")
185
+ cmap = cmaps.gray
186
+ norm = LogNorm(vmin=1e-3, vmax=1)
187
+ axsc.set_cmap(cmap)
188
+ axsc.set_norm(norm)
189
+ cb = fig.colorbar(cmaps.ScalarMappable(norm=norm, cmap=cmap), ax=ax)
190
+ cb.ax.set_title("Img. Mag.", y=1.02)
191
+
192
+
193
+ # frame animation
194
+ def update(i):
195
+ tcutt = fvals == freqs[i]
196
+ data = np.stack([txvals[tcutt] * scaling, tyvals[tcutt] * scaling]).T
197
+ axsc.set_offsets(data)
198
+ axsc.set_array(np.abs(magvals[tcutt]))
199
+ ax.set_title(f"Freq: {freqs[i]/framescale:.0f} [{framestr}Hz]", size=14)
200
+ return (axsc,)
201
+
202
+
203
+ # image framing
204
+ image_duration = 2 # seconds
205
+ frame_interval = 30e-3 # seconds between frames
206
+ total_aniframes = image_duration / frame_interval
207
+ stepsize = np.ceil(freqs.size / total_aniframes).astype(int)
208
+
209
+ if stepsize == 0:
210
+ stepsize = 1
211
+
212
+ frame_numbers = np.arange(0, freqs.size, step=stepsize)
213
+
214
+ ani = matplotlib.animation.FuncAnimation(
215
+ fig, update, frames=frame_numbers, interval=30, blit=True
216
+ )
217
+
218
+ # save
219
+ save_path = Path.cwd()
220
+ save_path = save_path / "multilens_spatial_freqslice.gif"
221
+ ani.save(filename=str(save_path), writer="pillow")
222
+
223
+
224
+ """
225
+ ###########################
226
+ #### Animate Temporal #####
227
+ ###########################
228
+ """
229
+ # setup time
230
+ total_frames = 100
231
+ left_edge = -total_frames // 4
232
+ right_edge = total_frames + left_edge
233
+ time_res = 2.56e-6 # s
234
+ trange = np.arange(-1, total_frames + 2) * time_res + left_edge * time_res
235
+
236
+ # setup figure
237
+ fig = plt.figure()
238
+ ax = fig.add_subplot(111)
239
+ axsc = ax.scatter([], [], s=4)
240
+ time_axis_scale = 1e-3
241
+ time_axis_str = "m"
242
+ freq_axis_scale = 1e6
243
+ freq_axis_str = "M"
244
+ ax.set_ylim(400, 800)
245
+ ax.set_xlim(
246
+ left_edge * time_res / time_axis_scale, right_edge * time_res / time_axis_scale
247
+ )
248
+ ax.set_ylabel(f"Freq. [{freq_axis_str}Hz]")
249
+ ax.set_xlabel(f"Time [{time_axis_str}s]")
250
+ cmap = cmaps.binary
251
+ norm = LogNorm(vmin=1e-3, vmax=1)
252
+ axsc.set_cmap(cmap)
253
+ axsc.set_norm(norm)
254
+ cb = fig.colorbar(cmaps.ScalarMappable(norm=norm, cmap=cmap), ax=ax)
255
+ cb.ax.set_title("Img. Mag.")
256
+
257
+ # image framing
258
+ image_duration = 2 # seconds
259
+ frame_interval = 30e-3 # seconds between frames
260
+ total_aniframes = image_duration / frame_interval
261
+ stepsize = np.ceil((trange.size - 1) / total_aniframes).astype(int)
262
+
263
+ if stepsize == 0:
264
+ stepsize = 1
265
+
266
+ trange_inds = np.arange(0, trange.size - 1, step=stepsize)
267
+
268
+
269
+ # animate frame
270
+ def update(i):
271
+ tcutt = (delayvals > trange[trange_inds[i]]) * (
272
+ delayvals <= trange[trange_inds[i + 1]]
273
+ )
274
+ data = np.stack(
275
+ [delayvals[tcutt] / time_axis_scale, fvals[tcutt] / freq_axis_scale]
276
+ ).T
277
+ axsc.set_offsets(data)
278
+ axsc.set_array(np.abs(magvals[tcutt]))
279
+ return (axsc,)
280
+
281
+
282
+ # animate
283
+ ani = matplotlib.animation.FuncAnimation(
284
+ fig, update, frames=trange_inds.size - 1, interval=30, blit=True
285
+ )
286
+
287
+ # save
288
+ save_path = Path.cwd()
289
+ save_path = save_path / "multilens_baseband_arrival.gif"
290
+ ani.save(filename=str(save_path), writer="pillow")
291
+
292
+
293
+ """
294
+ #######################################
295
+ #### Animate Temporal and Spatial #####
296
+ #######################################
297
+ """
298
+ # setup time
299
+ total_frames = 100
300
+ left_edge = -total_frames // 4
301
+ right_edge = total_frames + left_edge
302
+ time_res = 2.56e-6 # s
303
+ trange = np.arange(-1, total_frames + 2) * time_res + left_edge * time_res
304
+ fmin = np.amin(fvals)
305
+
306
+ # Setup plot
307
+ fig = plt.figure()
308
+ ax = fig.add_subplot(111)
309
+ axsc = ax.scatter([], [], s=4)
310
+ axisscale = 1e-6
311
+ axisstr = "milli"
312
+ framescale = 1e6
313
+ framestr = "M"
314
+ scaling = scale_r1 * 206264.806247 / axisscale
315
+
316
+ # Select largest spatial extent
317
+ cut1 = fvals == np.amin(fmin) # lowest freq for scattering
318
+ maxv_ = (
319
+ max(np.amax(np.abs(txvals[cut1])), np.amax(np.abs(tyvals[cut1]))) * scaling * 1.1
320
+ )
321
+
322
+ # Set axes and plot
323
+ ax.set_ylim(-maxv_, maxv_)
324
+ ax.set_xlim(-maxv_, maxv_)
325
+ ax.set_ylabel(f"$\\theta_Y$ [{axisstr}arcsec]", size=14)
326
+ ax.set_xlabel(f"$\\theta_X$ [{axisstr}arcsec]", size=14)
327
+ ax.set_facecolor("black")
328
+ cmap = cmaps.gray
329
+ norm = LogNorm(vmin=1e-3, vmax=1)
330
+ axsc.set_cmap(cmap)
331
+ axsc.set_norm(norm)
332
+ cb = fig.colorbar(cmaps.ScalarMappable(norm=norm, cmap=cmap), ax=ax)
333
+ cb.ax.set_title("Img. Mag.", y=1.02)
334
+
335
+ # image framing
336
+ image_duration = 2 # seconds
337
+ frame_interval = 30e-3 # seconds between frames
338
+ total_aniframes = image_duration / frame_interval
339
+ stepsize = np.ceil((trange.size - 1) / total_aniframes).astype(int)
340
+
341
+ if stepsize == 0:
342
+ stepsize = 1
343
+
344
+ trange_inds = np.arange(0, trange.size - 1, step=stepsize)
345
+
346
+
347
+ # frame animation
348
+ def update(i):
349
+ tcutt = (delayvals[cut1] > trange[trange_inds[i]]) * (
350
+ delayvals[cut1] <= trange[trange_inds[i + 1]]
351
+ )
352
+
353
+ data = np.stack([txvals[cut1][tcutt] * scaling, tyvals[cut1][tcutt] * scaling]).T
354
+ axsc.set_offsets(data)
355
+ axsc.set_array(np.abs(magvals[cut1][tcutt]))
356
+ ax.set_title(f"Freq: {fmin/framescale:.0f} [{framestr}Hz] ", size=14)
357
+ return (axsc,)
358
+
359
+
360
+ # animate
361
+ ani = matplotlib.animation.FuncAnimation(
362
+ fig, update, frames=trange_inds.size - 1, interval=30, blit=True
363
+ )
364
+
365
+ # save
366
+ save_path = Path.cwd()
367
+ save_path = save_path / "multilens_baseband_spatial_arrival.gif"
368
+ ani.save(filename=str(save_path), writer="pillow")