Skip to content

Commit 1be496a

Browse files
authored
Merge branch 'main' into normalization
2 parents 968625f + 37a740c commit 1be496a

File tree

12 files changed

+370
-91
lines changed

12 files changed

+370
-91
lines changed

README.md

Lines changed: 0 additions & 40 deletions
This file was deleted.

README.rst

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
=========================
2+
pyvisgen |ci| |codecov|
3+
=========================
4+
5+
.. |ci| image:: https://github.com/radionets-project/pyvisgen/workflows/CI/badge.svg?branch=main
6+
:target: https://github.com/radionets-project/pyvisgen/actions/workflows/ci.yml?branch=main
7+
:alt: Test Status
8+
9+
.. |codecov| image:: https://codecov.io/github/radionets-project/pyvisgen/badge.svg
10+
:target: https://codecov.io/github/radionets-project/pyvisgen
11+
:alt: Code coverage
12+
13+
14+
Python implementation of the VISGEN tool developed at `Haystack Observatory <https://www.haystack.mit.edu/astronomy/>`_.
15+
It uses the Radio Interferometer Measurement Equation (RIME) to simulate the measurement process of a radio interferometer.
16+
A gridder is also implemented to process the resulting visibilities and convert them to images suitable as input for
17+
the neural networks developed in the `radionets repository <https://github.com/radionets-project/radionets>`_.
18+
19+
Installation
20+
============
21+
22+
You can install the necessary packages in a conda environment of your choice by executing
23+
24+
.. code::
25+
26+
$ pip install -e .
27+
28+
29+
Usage
30+
=====
31+
32+
There are 3 possible modes at the moment: ``simulate`` (default), ``slurm``, and ``gridding``. ``simulate`` and ``slurm`` both
33+
utilize the RIME formalism for creating visibilities data. With the option ``gridding``, these visibilities get gridded and prepared
34+
as input images for training a neural network from the radionets framework. The necessary options and variables are set with a ``toml``
35+
file. An exemplary file can be found in ``config/data_set.toml``.
36+
37+
.. code::
38+
39+
$ pyvisgen_create_dataset --mode=simulate some_file.toml
40+
41+
42+
In the examples directory, you can find introductory jupyter notebooks which can be used as an entry point.
43+
44+
Input images
45+
============
46+
47+
As input images for the RIME formalism, we use GAN-generated radio galaxies created by `Rustige et. al. <https://doi.org/10.1093/rasti/rzad016>`_
48+
and `Kummer et. al. <https://doi.org/10.18420/inf2022_38>`_. Below, you can see four example images consisting of FRI and FRII sources.
49+
50+
.. image:: https://github.com/radionets-project/pyvisgen/assets/23259659/285e36f6-74e7-45f1-9976-896a38217880
51+
:alt: sources
52+
53+
Any image can be used as input for the formalism, as long as they are stored in the h5 format, generated with |h5py|_.
54+
55+
.. |h5py| replace:: ``h5py``
56+
.. _h5py: https://www.h5py.org/
57+
58+
RIME
59+
====
60+
61+
Currently, we use the following expression for the simulation process:
62+
63+
$$\\mathbf{V}_{\\mathrm{pq}}(l, m) = \\sum_{l, m} \\mathbf{E}_{\\mathrm{p}}(l, m) \\mathbf{K}_{\\mathrm{p}}(l, m) \\mathbf{B}(l, m) \\mathbf{K}^{H}_{\\mathrm{q}}(l, m) \\mathbf{E}^{H}_{\\mathrm{q}}(l, m)$$
64+
65+
Here, $\\mathbf{B}(l, m)$ corresponds to the source distribution, $\\mathbf{K}(l, m) = \\exp(-2\\pi\\cdot i\\cdot (ul + vm))$ represents
66+
the phase delay, and $\\mathbf{E}(l, m) = \\mathrm{jinc}\\left(\\frac{2\\pi}{\\lambda}d\\cdot \\theta_{lm}\\right)$ the telescope properties,
67+
with $\\mathrm{jinc(x)} = \\frac{J_1(x)}{x}$ and $J_1(x)$ as the first Bessel function. An exemplary result can be found below.
68+
69+
.. image:: https://github.com/radionets-project/pyvisgen/assets/23259659/858a5d4b-893a-4216-8d33-41d33981354c
70+
:alt: visibilities
71+
72+
Visualization of Jones matrices
73+
===============================
74+
75+
In this section, you can see visualizations of the matrices $\\mathbf{E}(l, m)$ and $\\mathbf{K}(l, m)$.
76+
77+
Visualization of the $\\mathbf{E}$ matrix
78+
-----------------------------------------
79+
.. image:: https://github.com/radionets-project/pyvisgen/assets/23259659/194a321b-77cd-423b-9d01-c18c0741d6c5
80+
:alt: visualize_E
81+
82+
Visualization of the $\\mathbf{K}$ matrix
83+
-----------------------------------------
84+
.. image:: https://github.com/radionets-project/pyvisgen/assets/23259659/501f487a-498b-4143-b54a-eb0e2f28e417
85+
:alt: visualize_K

docs/changes/45.maintenance.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Switch README to reStructuredText
2+
- Add Codecov badge

docs/changes/46.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- ``pyvisgen.layouts.get_array_layout`` now also accepts custom layouts stored in a ``pd.DataFrame``

docs/changes/48.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Added optional auto scaling for batchsize in vis_loop

environment.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ dependencies:
1616
- pytest
1717
- pytest-cov
1818
- pytest-runner
19+
- pip:
20+
- toma

pyproject.toml

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,26 @@ classifiers = [
2828
requires-python = ">=3.10"
2929

3030
dependencies = [
31-
"numpy",
31+
"astroplan",
3232
"astropy<=6.1.0",
33-
"torch",
34-
"matplotlib",
33+
"click",
34+
"h5py",
3535
"ipython",
36-
"scipy",
36+
"jupyter",
37+
"matplotlib",
38+
"natsort",
39+
"numexpr",
40+
"numpy",
3741
"pandas",
38-
"toml",
42+
"pre-commit",
3943
"pytest",
4044
"pytest-cov",
41-
"jupyter",
42-
"astroplan",
45+
"scipy",
46+
"toma",
47+
"toml",
48+
"torch",
4349
"torch",
4450
"tqdm",
45-
"numexpr",
46-
"click",
47-
"h5py",
48-
"natsort",
49-
"pre-commit",
5051
]
5152

5253
[project.scripts]

pyvisgen/layouts/layouts.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,46 @@ def __getitem__(self, i):
2424
return Stations(*[getattr(self, f.name)[i] for f in fields(self)])
2525

2626

27-
def get_array_layout(array_name, writer=False):
27+
def get_array_layout(array_layout: str | Path | pd.DataFrame, writer: bool = False):
2828
"""Reads telescope layout txt file and converts it into a dataclass.
29+
Also allows a DataFrame to be passed that is then converted into a dataclass
30+
object.
2931
Available arrays:
3032
- EHT
3133
3234
Parameters
3335
----------
34-
array_name : str
35-
Name of telescope array
36+
array_layout : str or pathlib.Path or pd.DataFrame
37+
Name of telescope array or pd.DataFrame containing
38+
the array layout.
39+
writer : bool, optional
40+
If ``True``, return ``array`` DataFrame instead of
41+
``Stations`` dataclass object.
3642
3743
Returns
3844
-------
3945
dataclass objects
40-
Station infos combinde in dataclass
46+
Station infos combined in dataclass
4147
"""
42-
f = array_name + ".txt"
43-
array = pd.read_csv(file_dir / f, sep=r"\s+")
44-
if array_name == "vla":
45-
loc = EarthLocation.of_site("VLA")
46-
array["X"] += loc.value[0]
47-
array["Y"] += loc.value[1]
48-
array["Z"] += loc.value[2]
49-
50-
if array_name == "test_layout":
51-
loc = EarthLocation.of_address("dortmund")
52-
array["X"] += loc.value[0]
53-
array["Y"] += loc.value[1]
54-
array["Z"] += loc.value[2]
48+
if isinstance(array_layout, str):
49+
f = array_layout + ".txt"
50+
array = pd.read_csv(file_dir / f, sep=r"\s+")
51+
52+
if array_layout == "vla":
53+
# Change relative positions to absolute positions
54+
# for the VLA layout
55+
loc = EarthLocation.of_site("VLA")
56+
array["X"] += loc.value[0]
57+
array["Y"] += loc.value[1]
58+
array["Z"] += loc.value[2]
59+
60+
elif isinstance(array_layout, pd.DataFrame):
61+
array = array_layout
62+
else:
63+
raise TypeError(
64+
"Expected array_layout to be of type str, "
65+
"pathlib.Path, or pandas.DataFrame!"
66+
)
5567

5668
# drop name col and convert to tensor
5769
tensor = torch.from_numpy(array.iloc[:, 1:].values)

pyvisgen/simulation/visibility.py

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from dataclasses import dataclass, fields
22

33
import torch
4-
from tqdm import tqdm
4+
import toma
5+
from tqdm.autonotebook import tqdm
56

67
import pyvisgen.simulation.scan as scan
78

@@ -44,13 +45,19 @@ def vis_loop(
4445
num_threads=10,
4546
noisy=True,
4647
mode="full",
47-
batch_size=100,
48+
batch_size="auto",
4849
show_progress=False,
4950
normalize=True,
5051
):
5152
torch.set_num_threads(num_threads)
5253
torch._dynamo.config.suppress_errors = True
5354

55+
if not (
56+
isinstance(batch_size, int)
57+
or (isinstance(batch_size, str) and batch_size == "auto")
58+
):
59+
raise ValueError("Expected batch_size to be 'auto' or of type int")
60+
5461
SI = torch.flip(SI, dims=[1])
5562

5663
# define unpolarized sky distribution
@@ -106,10 +113,78 @@ def vis_loop(
106113
else:
107114
raise ValueError("Unsupported mode!")
108115

109-
batches = torch.arange(bas[:].shape[1]).split(batch_size)
116+
if batch_size == "auto":
117+
batch_size = bas[:].shape[1]
118+
119+
visibilities = toma.explicit.batch(
120+
_batch_loop,
121+
batch_size,
122+
visibilities,
123+
vis_num,
124+
obs,
125+
B,
126+
bas,
127+
lm,
128+
rd,
129+
noisy,
130+
show_progress,
131+
)
132+
133+
return visibilities
110134

111-
if show_progress:
112-
batches = tqdm(batches)
135+
136+
def _batch_loop(
137+
batch_size: int,
138+
visibilities,
139+
vis_num: int,
140+
obs,
141+
B: torch.tensor,
142+
bas,
143+
lm: torch.tensor,
144+
rd: torch.tensor,
145+
noisy: bool | float,
146+
show_progress: bool,
147+
):
148+
"""Main simulation loop of pyvisgen. Computes visibilities
149+
batchwise.
150+
151+
Parameters
152+
----------
153+
batch_size : int
154+
Batch size for loop over Baselines dataclass object.
155+
visibilities : Visibilities
156+
Visibilities dataclass object.
157+
vis_num : int
158+
Number of visibilities.
159+
obs : Observation
160+
Observation class object.
161+
B : torch.tensor
162+
Stokes matrix containing stokes visibilities.
163+
bas : Baselines
164+
Baselines dataclass object.
165+
lm : torch.tensor
166+
lm grid.
167+
rd : torch.tensor
168+
rd grid.
169+
noisy : float or bool
170+
Simulate noise as SEFD with given value. If set to False,
171+
no noise is simulated.
172+
show_progress :
173+
If True, show a progress bar tracking the loop.
174+
175+
Returns
176+
-------
177+
visibilities : Visibilities
178+
Visibilities dataclass object.
179+
"""
180+
batches = torch.arange(bas[:].shape[1]).split(batch_size)
181+
batches = tqdm(
182+
batches,
183+
position=0,
184+
disable=not show_progress,
185+
desc="Computing visibilities",
186+
postfix=f"Batch size: {batch_size}",
187+
)
113188

114189
for p in batches:
115190
bas_p = bas[:][:, p]
@@ -157,6 +232,7 @@ def vis_loop(
157232

158233
visibilities.add(vis)
159234
del int_values
235+
160236
return visibilities
161237

162238

tests/data/test_layout.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
station_name X Y Z dish_dia el_low el_high SEFD altitude
2+
t000 -4000 2000 0 25 15.0 85.0 110.0 1000.0
3+
t001 8000 -2000 0 25 15.0 85.0 110.0 1000.0
4+
t002 1000 1000 0 25 15.0 85.0 110.0 1000.0
5+
t003 -4000 6000 0 25 15.0 85.0 110.0 1000.0
6+
t004 -3000 -3000 0 25 15.0 85.0 110.0 1000.0
7+
t005 6000 -5000 0 25 15.0 85.0 110.0 1000.0
8+
t006 8000 2000 0 25 15.0 85.0 110.0 1000.0
9+
t007 2000 -4000 0 25 15.0 85.0 110.0 1000.0
10+
t008 -6000 3000 0 25 15.0 85.0 110.0 1000.0
11+
t009 -2000 8000 0 25 15.0 85.0 110.0 1000.0

0 commit comments

Comments
 (0)