plotastrodata 1.9.13__tar.gz → 1.9.15__tar.gz
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.
- {plotastrodata-1.9.13/plotastrodata.egg-info → plotastrodata-1.9.15}/PKG-INFO +1 -1
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/__init__.py +1 -1
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/analysis_utils.py +42 -35
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/ext_utils.py +19 -4
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/fft_utils.py +14 -14
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/fits_utils.py +35 -26
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/fitting_utils.py +46 -36
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/los_utils.py +3 -3
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/matrix_utils.py +4 -4
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/noise_utils.py +34 -18
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/other_utils.py +23 -10
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/plot_utils.py +175 -66
- {plotastrodata-1.9.13 → plotastrodata-1.9.15/plotastrodata.egg-info}/PKG-INFO +1 -1
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/LICENSE +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/MANIFEST.in +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/README.md +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/const_utils.py +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/coord_utils.py +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/SOURCES.txt +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/dependency_links.txt +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/not-zip-safe +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/requires.txt +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/top_level.txt +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/setup.cfg +0 -0
- {plotastrodata-1.9.13 → plotastrodata-1.9.15}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.15
|
|
4
4
|
Summary: plotastrodata is a tool for astronomers to create figures from FITS files and perform fundamental data analyses with ease.
|
|
5
5
|
Home-page: https://github.com/yusukeaso-astron/plotastrodata
|
|
6
6
|
Download-URL: https://github.com/yusukeaso-astron/plotastrodata
|
|
@@ -3,7 +3,7 @@ import warnings
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
5
5
|
from scipy.signal import convolve
|
|
6
|
-
from typing import Callable
|
|
6
|
+
from typing import Any, Callable
|
|
7
7
|
|
|
8
8
|
from plotastrodata import const_utils as cu
|
|
9
9
|
from plotastrodata.coord_utils import coord2xy, rel2abs, xy2coord
|
|
@@ -18,7 +18,7 @@ from plotastrodata.other_utils import (isdeg, nearest_index,
|
|
|
18
18
|
|
|
19
19
|
def quadrantmean(data: np.ndarray, x: np.ndarray, y: np.ndarray,
|
|
20
20
|
quadrants: str = '13'
|
|
21
|
-
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
21
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray] | None:
|
|
22
22
|
"""Take mean between 1st and 3rd (or 2nd and 4th) quadrants.
|
|
23
23
|
|
|
24
24
|
Args:
|
|
@@ -52,7 +52,7 @@ def quadrantmean(data: np.ndarray, x: np.ndarray, y: np.ndarray,
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
def filled2d(data: np.ndarray, x: np.ndarray, y: np.ndarray, n: int = 1,
|
|
55
|
-
**kwargs) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
55
|
+
**kwargs: Any) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
56
56
|
"""Fill 2D data, 1D x, and 1D y by a factor of n using RGI.
|
|
57
57
|
|
|
58
58
|
Args:
|
|
@@ -71,8 +71,8 @@ def filled2d(data: np.ndarray, x: np.ndarray, y: np.ndarray, n: int = 1,
|
|
|
71
71
|
return d, xnew, ynew
|
|
72
72
|
|
|
73
73
|
|
|
74
|
-
def _need_multipixels(method):
|
|
75
|
-
def wrapper(cls, *args, **kwargs):
|
|
74
|
+
def _need_multipixels(method: Callable) -> Callable:
|
|
75
|
+
def wrapper(cls: Any, *args: Any, **kwargs: Any) -> Any | None:
|
|
76
76
|
singlepixel = cls.dx is None or cls.dy is None
|
|
77
77
|
if singlepixel:
|
|
78
78
|
print('No pixel size.')
|
|
@@ -90,7 +90,7 @@ class AstroData():
|
|
|
90
90
|
x (np.ndarray, optional): 1D array. Defaults to None.
|
|
91
91
|
y (np.ndarray, optional): 1D array. Defaults to None.
|
|
92
92
|
v (np.ndarray, optional): 1D array. Defaults to None.
|
|
93
|
-
beam (np.ndarray, optional): [bmaj, bmin, bpa]. Defaults
|
|
93
|
+
beam (np.ndarray, optional): [bmaj, bmin, bpa]. Defaults to [None, None, None].
|
|
94
94
|
fitsimage (str, optional): Input fits name. Defaults to None.
|
|
95
95
|
Tb (bool, optional): True means the data array is brightness temperature. Defaults to False.
|
|
96
96
|
sigma (float or str, optional): Noise level or method for measuring it. Defaults to 'hist'.
|
|
@@ -116,7 +116,7 @@ class AstroData():
|
|
|
116
116
|
pv: bool = False
|
|
117
117
|
bunit: str = ''
|
|
118
118
|
|
|
119
|
-
def __post_init__(self):
|
|
119
|
+
def __post_init__(self) -> None:
|
|
120
120
|
n = 0
|
|
121
121
|
if self.fitsimage is not None:
|
|
122
122
|
if not isinstance(self.fitsimage, list):
|
|
@@ -142,7 +142,7 @@ class AstroData():
|
|
|
142
142
|
self.beam_org = None
|
|
143
143
|
self.fitsheader = None
|
|
144
144
|
|
|
145
|
-
def _binning_one(self, t: str, width: float):
|
|
145
|
+
def _binning_one(self, t: str, width: float) -> None:
|
|
146
146
|
grid = getattr(self, t)
|
|
147
147
|
if width == 1 or grid is None:
|
|
148
148
|
return
|
|
@@ -168,7 +168,7 @@ class AstroData():
|
|
|
168
168
|
setattr(self, t, gridnew / width)
|
|
169
169
|
setattr(self, dt, sep * width)
|
|
170
170
|
|
|
171
|
-
def binning(self, width: list[int] = [1, 1, 1]):
|
|
171
|
+
def binning(self, width: list[int] = [1, 1, 1]) -> None:
|
|
172
172
|
"""Binning up neighboring pixels in the v, y, and x domain.
|
|
173
173
|
|
|
174
174
|
Args:
|
|
@@ -202,7 +202,7 @@ class AstroData():
|
|
|
202
202
|
|
|
203
203
|
def centering(self, includexy: bool = True,
|
|
204
204
|
includev: bool = False,
|
|
205
|
-
**kwargs):
|
|
205
|
+
**kwargs: Any) -> None:
|
|
206
206
|
"""Spatial regridding to set the center at (x,y,v)=(0,0,0).
|
|
207
207
|
|
|
208
208
|
Args:
|
|
@@ -238,7 +238,7 @@ class AstroData():
|
|
|
238
238
|
print('No change because includexy=False and includev=False.')
|
|
239
239
|
|
|
240
240
|
@_need_multipixels
|
|
241
|
-
def circularbeam(self):
|
|
241
|
+
def circularbeam(self) -> None:
|
|
242
242
|
"""Make the beam circular by convolving with 1D Gaussian
|
|
243
243
|
"""
|
|
244
244
|
if None in self.beam:
|
|
@@ -262,7 +262,8 @@ class AstroData():
|
|
|
262
262
|
self.beam[1] = self.beam[0]
|
|
263
263
|
self.beam[2] = 0
|
|
264
264
|
|
|
265
|
-
def deproject(self, pa: float = 0, incl: float = 0,
|
|
265
|
+
def deproject(self, pa: float = 0, incl: float = 0,
|
|
266
|
+
**kwargs: Any) -> None:
|
|
266
267
|
"""Exapnd by a factor of 1/cos(incl) in the direction of pa+90 deg.
|
|
267
268
|
|
|
268
269
|
Args:
|
|
@@ -294,9 +295,12 @@ class AstroData():
|
|
|
294
295
|
def fit2d(self, model: Callable, bounds: np.ndarray,
|
|
295
296
|
progressbar: bool = False,
|
|
296
297
|
kwargs_fit: dict = {}, kwargs_plotcorner: dict = {},
|
|
297
|
-
chan: int | None = None):
|
|
298
|
+
chan: int | None = None) -> dict[str, Any] | None:
|
|
298
299
|
"""Fit a given 2D model function to self.data.
|
|
299
300
|
|
|
301
|
+
Default keyword values:
|
|
302
|
+
kwargs_plotcorner: ``show=False`` and ``savefig=None``. User-supplied values in ``kwargs_plotcorner`` override these defaults.
|
|
303
|
+
|
|
300
304
|
Args:
|
|
301
305
|
model (function): The model function in the form of f(par, x, y).
|
|
302
306
|
bounds (np.ndarray): bounds for fitting_utils.EmceeCorner.
|
|
@@ -320,7 +324,7 @@ class AstroData():
|
|
|
320
324
|
+ ' This correction is relatively conservative.'
|
|
321
325
|
warnings.warn(s, UserWarning)
|
|
322
326
|
|
|
323
|
-
def logl(p):
|
|
327
|
+
def logl(p: np.ndarray) -> float:
|
|
324
328
|
rss = np.nansum((model(p, x, y) - d)**2)
|
|
325
329
|
return -0.5 * rss / self.sigma**2 / pixelperbeam
|
|
326
330
|
|
|
@@ -374,7 +378,7 @@ class AstroData():
|
|
|
374
378
|
'model': model, 'residual': residual,
|
|
375
379
|
'center': newcenter}
|
|
376
380
|
|
|
377
|
-
def histogram(self, **kwargs) -> tuple:
|
|
381
|
+
def histogram(self, **kwargs: Any) -> tuple[np.ndarray, np.ndarray]:
|
|
378
382
|
"""Output histogram of self.data using numpy.histogram. This method can take the arguments of numpy.histogram.
|
|
379
383
|
|
|
380
384
|
Returns:
|
|
@@ -387,12 +391,12 @@ class AstroData():
|
|
|
387
391
|
|
|
388
392
|
def mask(self, dataformask: np.ndarray | None = None,
|
|
389
393
|
includepix: list[float, float] = [],
|
|
390
|
-
excludepix: list[float, float] = []):
|
|
394
|
+
excludepix: list[float, float] = []) -> None:
|
|
391
395
|
"""Mask self.data using a 2D or 3D array of dataformask.
|
|
392
396
|
|
|
393
397
|
Args:
|
|
394
398
|
dataformask (np.ndarray, optional): 2D or 3D array is used for specifying the mask.
|
|
395
|
-
includepix (list, optional): Data in this range
|
|
399
|
+
includepix (list, optional): Data in this range survives. Defaults to [].
|
|
396
400
|
excludepix (list, optional): Data in this range is masked. Defaults to [].
|
|
397
401
|
"""
|
|
398
402
|
if dataformask is None:
|
|
@@ -411,7 +415,7 @@ class AstroData():
|
|
|
411
415
|
if len(excludepix) == 2:
|
|
412
416
|
self.data[(excludepix[0] < mask) * (mask < excludepix[1])] = np.nan
|
|
413
417
|
|
|
414
|
-
def _gfit_profile(self, prof: list, gaussfit: bool):
|
|
418
|
+
def _gfit_profile(self, prof: list, gaussfit: bool) -> dict[str, Any]:
|
|
415
419
|
if not gaussfit:
|
|
416
420
|
return {}
|
|
417
421
|
|
|
@@ -431,7 +435,7 @@ class AstroData():
|
|
|
431
435
|
ellipse: list[float, float, float] | None = None,
|
|
432
436
|
ninterp: int = 1,
|
|
433
437
|
flux: bool = False, gaussfit: bool = False
|
|
434
|
-
) -> tuple[np.ndarray, np.ndarray, dict]:
|
|
438
|
+
) -> tuple[np.ndarray, np.ndarray, dict] | None:
|
|
435
439
|
"""Get a list of line profiles at given spatial coordinates.
|
|
436
440
|
|
|
437
441
|
Args:
|
|
@@ -477,7 +481,7 @@ class AstroData():
|
|
|
477
481
|
gfitres = self._gfit_profile(prof, gaussfit)
|
|
478
482
|
return self.v, prof, gfitres
|
|
479
483
|
|
|
480
|
-
def rotate(self, pa: float = 0, **kwargs):
|
|
484
|
+
def rotate(self, pa: float = 0, **kwargs: Any) -> None:
|
|
481
485
|
"""Counter clockwise rotation with respect to the center.
|
|
482
486
|
|
|
483
487
|
Args:
|
|
@@ -489,7 +493,7 @@ class AstroData():
|
|
|
489
493
|
self.beam[2] = self.beam[2] + pa
|
|
490
494
|
|
|
491
495
|
def slice(self, length: float = 0, pa: float = 0,
|
|
492
|
-
dx: float | None = None, **kwargs) -> np.ndarray:
|
|
496
|
+
dx: float | None = None, **kwargs: Any) -> np.ndarray | None:
|
|
493
497
|
"""Get 1D slice with given a length and a position-angle.
|
|
494
498
|
|
|
495
499
|
Args:
|
|
@@ -557,7 +561,7 @@ class AstroData():
|
|
|
557
561
|
"""Write out the AstroData to a FITS file.
|
|
558
562
|
|
|
559
563
|
Args:
|
|
560
|
-
fitsimage (str, optional): Output FITS file name. Defaults to 'out.fits'.
|
|
564
|
+
fitsimage (str, optional): Output FITS file name. Existing files with the same name are overwritten. Defaults to 'out.fits'.
|
|
561
565
|
header (dict, optional): Header dictionary. Defaults to {}.
|
|
562
566
|
"""
|
|
563
567
|
h = {}
|
|
@@ -588,18 +592,18 @@ class AstroData():
|
|
|
588
592
|
fitsimage=fitsimage)
|
|
589
593
|
|
|
590
594
|
|
|
591
|
-
def _as_list(value, n: int, isbeam: bool = False):
|
|
595
|
+
def _as_list(value: Any, n: int, isbeam: bool = False) -> Any:
|
|
592
596
|
if isbeam:
|
|
593
597
|
return [value] * n if np.ndim(value) == 1 else value
|
|
594
598
|
else:
|
|
595
599
|
return value if isinstance(value, list) else [value] * n
|
|
596
600
|
|
|
597
601
|
|
|
598
|
-
def _scalar_if_single(value, n: int):
|
|
602
|
+
def _scalar_if_single(value: Any, n: int) -> Any:
|
|
599
603
|
return value[0] if n == 1 else value
|
|
600
604
|
|
|
601
605
|
|
|
602
|
-
def _get_gridsep(axis: np.ndarray | None):
|
|
606
|
+
def _get_gridsep(axis: np.ndarray | None) -> float | None:
|
|
603
607
|
return axis[1] - axis[0] if axis is not None and len(axis) > 1 else None
|
|
604
608
|
|
|
605
609
|
|
|
@@ -618,7 +622,7 @@ class AstroFrame():
|
|
|
618
622
|
vsys (float, optional): Each channel shows v-vsys. Defaults to 0..
|
|
619
623
|
center (str, optional): Central coordinate like '12h34m56.7s 12d34m56.7s'. Defaults to None.
|
|
620
624
|
fitsimage (str, optional): Fits to get center. Defaults to None.
|
|
621
|
-
rmax (float, optional): The x range is [-rmax, rmax]. The y range is [-rmax,
|
|
625
|
+
rmax (float, optional): The x range is [-rmax, rmax]. The y range is [-rmax, rmax]. Defaults to 1e10.
|
|
622
626
|
xmax (float, optional): The x range is [xmin, xmax]. Defaults to None.
|
|
623
627
|
xmin (float, optional): The x range is [xmin, xmax]. Defaults to None.
|
|
624
628
|
ymax (float, optional): The y range is [ymin, ymax]. Defaults to None.
|
|
@@ -651,7 +655,7 @@ class AstroFrame():
|
|
|
651
655
|
pv: bool = False
|
|
652
656
|
quadrants: str | None = None
|
|
653
657
|
|
|
654
|
-
def __post_init__(self):
|
|
658
|
+
def __post_init__(self) -> None:
|
|
655
659
|
self.xdir = -1 if self.xflip else 1
|
|
656
660
|
self.ydir = -1 if self.yflip else 1
|
|
657
661
|
self.xmin = -self.rmax if self.xmin is None else self.xmin
|
|
@@ -699,7 +703,7 @@ class AstroFrame():
|
|
|
699
703
|
x[i], y[i] = rel2abs(*p, self.Xlim, self.Ylim)
|
|
700
704
|
return np.array([x, y])
|
|
701
705
|
|
|
702
|
-
def _get_restfreq(self, header: dict):
|
|
706
|
+
def _get_restfreq(self, header: dict) -> float | None:
|
|
703
707
|
"""Extract rest frequency from FITS header."""
|
|
704
708
|
if 'RESTFRQ' in header:
|
|
705
709
|
return header['RESTFRQ']
|
|
@@ -743,21 +747,22 @@ class AstroFrame():
|
|
|
743
747
|
d.center[i] = cnew
|
|
744
748
|
return grid
|
|
745
749
|
|
|
746
|
-
def _ascending_v(self, d: AstroData, i: int,
|
|
750
|
+
def _ascending_v(self, d: AstroData, i: int,
|
|
751
|
+
v: np.ndarray | None) -> None:
|
|
747
752
|
if v is not None and len(v) > 1 and v[1] < v[0]:
|
|
748
753
|
d.data[i], v = d.data[i][::-1], v[::-1]
|
|
749
754
|
print('Velocity has been inverted.')
|
|
750
755
|
d.v = v
|
|
751
756
|
|
|
752
757
|
def _xyskip(self, d: AstroData, i: int,
|
|
753
|
-
x: np.ndarray | None, y: np.ndarray | None):
|
|
758
|
+
x: np.ndarray | None, y: np.ndarray | None) -> None:
|
|
754
759
|
d.x = x[::self.xskip]
|
|
755
760
|
d.y = y[::self.yskip]
|
|
756
761
|
data = np.moveaxis(d.data[i], [-2, -1], [0, 1])
|
|
757
762
|
data = data[::self.yskip, ::self.xskip]
|
|
758
763
|
d.data[i] = np.moveaxis(data, [0, 1], [-2, -1])
|
|
759
764
|
|
|
760
|
-
def _trim_skip(self, d: AstroData, i: int, grid: list):
|
|
765
|
+
def _trim_skip(self, d: AstroData, i: int, grid: list) -> None:
|
|
761
766
|
d.data[i], grid = trim(data=d.data[i],
|
|
762
767
|
x=grid[0], y=grid[1], v=grid[2],
|
|
763
768
|
xlim=self.xlim, ylim=self.ylim,
|
|
@@ -773,7 +778,7 @@ class AstroFrame():
|
|
|
773
778
|
for axis in ['x', 'y', 'v']:
|
|
774
779
|
setattr(d, f'd{axis}', _get_gridsep(getattr(d, axis)))
|
|
775
780
|
|
|
776
|
-
def _convert_to_Tb(self, d: AstroData, i: int):
|
|
781
|
+
def _convert_to_Tb(self, d: AstroData, i: int) -> None:
|
|
777
782
|
"""Convert Jy/beam data to brightness temperature if requested."""
|
|
778
783
|
if not d.Tb[i]:
|
|
779
784
|
return
|
|
@@ -790,7 +795,7 @@ class AstroFrame():
|
|
|
790
795
|
if d.sigma[i] is not None:
|
|
791
796
|
d.sigma[i] = d.sigma[i] * factor
|
|
792
797
|
|
|
793
|
-
def _set_pv_beam(self, d: AstroData, i: int):
|
|
798
|
+
def _set_pv_beam(self, d: AstroData, i: int) -> None:
|
|
794
799
|
"""Set effective PV beam."""
|
|
795
800
|
if not self.pv or d.pv[i] or None in d.beam[i]:
|
|
796
801
|
return
|
|
@@ -803,7 +808,7 @@ class AstroFrame():
|
|
|
803
808
|
beam_incut = 1 / np.hypot(np.cos(angle) / bmaj, np.sin(angle) / bmin)
|
|
804
809
|
d.beam[i] = np.array([np.abs(d.dv), beam_incut, 0])
|
|
805
810
|
|
|
806
|
-
def _read_one(self, d: AstroData, i: int):
|
|
811
|
+
def _read_one(self, d: AstroData, i: int) -> None:
|
|
807
812
|
if d.center[i] == 'common':
|
|
808
813
|
d.center[i] = self.center
|
|
809
814
|
d.sigma_org[i] = d.sigma[i]
|
|
@@ -826,9 +831,11 @@ class AstroFrame():
|
|
|
826
831
|
d.fitsimage_org[i] = d.fitsimage[i]
|
|
827
832
|
d.fitsimage[i] = None
|
|
828
833
|
|
|
829
|
-
def read(self, d: AstroData, xskip: int = 1, yskip: int = 1):
|
|
834
|
+
def read(self, d: AstroData, xskip: int = 1, yskip: int = 1) -> None:
|
|
830
835
|
"""Get data, grid, sigma, beam, and bunit from AstroData, which is a part of the input of add_color, add_contour, add_segment, and add_rgb.
|
|
831
836
|
|
|
837
|
+
This method modifies ``d`` in place. During the read, the input fields are normalized to per-dataset lists, FITS-derived values are filled, trimming and coordinate-frame changes are applied, and bookkeeping fields such as ``fitsimage``, ``fitsimage_org``, ``Tb``, ``cfactor``, and ``sigma`` are updated.
|
|
838
|
+
|
|
832
839
|
Args:
|
|
833
840
|
d (AstroData): Dataclass for the add_* input.
|
|
834
841
|
xskip, yskip (int): Spatial pixel skip. Defaults to 1.
|
|
@@ -1,26 +1,41 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import shlex
|
|
3
3
|
import subprocess
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
from plotastrodata import const_utils as cu
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
def terminal(cmd: str,
|
|
9
|
+
def terminal(cmd: str, check: bool = True,
|
|
10
|
+
**kwargs: Any) -> subprocess.CompletedProcess:
|
|
9
11
|
"""Run a terminal command through subprocess.run.
|
|
10
12
|
|
|
13
|
+
The command is split with ``shlex.split`` and executed without a shell. It can still run destructive programs, so pass only trusted command strings.
|
|
14
|
+
|
|
11
15
|
Args:
|
|
12
16
|
cmd (str): Terminal command.
|
|
17
|
+
check (bool, optional): Whether to raise an exception if the command fails. Defaults to True.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
subprocess.CompletedProcess: Result from subprocess.run.
|
|
13
21
|
"""
|
|
14
|
-
subprocess.run(shlex.split(cmd), **kwargs)
|
|
22
|
+
return subprocess.run(shlex.split(cmd), check=check, **kwargs)
|
|
15
23
|
|
|
16
24
|
|
|
17
|
-
def runpython(filename: str,
|
|
25
|
+
def runpython(filename: str, check: bool = True,
|
|
26
|
+
**kwargs: Any) -> subprocess.CompletedProcess:
|
|
18
27
|
"""Run a python file.
|
|
19
28
|
|
|
29
|
+
The file is executed as a Python script and can perform arbitrary file or system operations. Pass only trusted files.
|
|
30
|
+
|
|
20
31
|
Args:
|
|
21
32
|
filename (str): Python file name.
|
|
33
|
+
check (bool, optional): Whether to raise an exception if the script fails. Defaults to True.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
subprocess.CompletedProcess: Result from subprocess.run.
|
|
22
37
|
"""
|
|
23
|
-
|
|
38
|
+
return subprocess.run(['python', filename], check=check, **kwargs)
|
|
24
39
|
|
|
25
40
|
|
|
26
41
|
def BnuT(T: float = 30, nu: float = 230e9) -> float:
|
|
@@ -163,7 +163,7 @@ def ifftcentering2(F: np.ndarray,
|
|
|
163
163
|
Args:
|
|
164
164
|
F (np.ndarray): 2D array. An FFT result in the frequency domain.
|
|
165
165
|
u (np.ndarray, optional): 1D or 2D array. The first frequency coordinate. Defaults to None.
|
|
166
|
-
v (np.ndarray, optional): 1D or 2D array. The second frequency
|
|
166
|
+
v (np.ndarray, optional): 1D or 2D array. The second frequency coordinate. Defaults to None.
|
|
167
167
|
xcenter (float, optional): x of phase reference (used in fftcentering2). Defaults to 0.
|
|
168
168
|
ycenter (float, optional): y of phase reference (used in fftcentering2). Defaults to 0.
|
|
169
169
|
x0 (float, optional): spatial coordinate of x[0]. Defaults to None.
|
|
@@ -221,7 +221,7 @@ class FftCentering():
|
|
|
221
221
|
y: np.ndarray | None = None,
|
|
222
222
|
xcenter: float = 0,
|
|
223
223
|
ycenter: float = 0,
|
|
224
|
-
rfft: bool = False):
|
|
224
|
+
rfft: bool = False) -> None:
|
|
225
225
|
nx = len(x)
|
|
226
226
|
self.x = x
|
|
227
227
|
self.dx = dx = x[1] - x[0]
|
|
@@ -252,14 +252,14 @@ class FftCentering():
|
|
|
252
252
|
np.ndarray: FFT result. When f is None, the return is the FFT function.
|
|
253
253
|
"""
|
|
254
254
|
if self.ndim == 1:
|
|
255
|
-
def func(f: np.ndarray):
|
|
255
|
+
def func(f: np.ndarray) -> np.ndarray:
|
|
256
256
|
F, _ = fftcentering(f=f, x=self.x,
|
|
257
257
|
xcenter=self.xcenter,
|
|
258
258
|
rfft=self.rfft)
|
|
259
259
|
return F
|
|
260
260
|
|
|
261
261
|
else:
|
|
262
|
-
def func(f: np.ndarray):
|
|
262
|
+
def func(f: np.ndarray) -> np.ndarray:
|
|
263
263
|
F, _, _ = fftcentering2(f=f, x=self.x, y=self.y,
|
|
264
264
|
xcenter=self.xcenter,
|
|
265
265
|
ycenter=self.ycenter,
|
|
@@ -279,7 +279,7 @@ class FftCentering():
|
|
|
279
279
|
np.ndarray: iFFT result. When F is None, the return is the iFFT function.
|
|
280
280
|
"""
|
|
281
281
|
if self.ndim == 1:
|
|
282
|
-
def func(F: np.ndarray):
|
|
282
|
+
def func(F: np.ndarray) -> np.ndarray:
|
|
283
283
|
f, _ = ifftcentering(F=F, u=self.u,
|
|
284
284
|
xcenter=self.xcenter,
|
|
285
285
|
x0=self.x[0],
|
|
@@ -289,7 +289,7 @@ class FftCentering():
|
|
|
289
289
|
return f
|
|
290
290
|
|
|
291
291
|
else:
|
|
292
|
-
def func(F: np.ndarray):
|
|
292
|
+
def func(F: np.ndarray) -> np.ndarray:
|
|
293
293
|
f, _, _ = ifftcentering2(F=F, u=self.u, v=self.v,
|
|
294
294
|
xcenter=self.xcenter,
|
|
295
295
|
ycenter=self.ycenter,
|
|
@@ -349,7 +349,7 @@ def fftfits(fitsimage: str, center: str | None = None, lam: float = 1,
|
|
|
349
349
|
lam (float, optional): Return u * lam and v * lam. Defaults to 1.
|
|
350
350
|
xlim (list, optional): Range of x for zero padding in arcsec.
|
|
351
351
|
ylim (list, optional): Range of y for zero padding in arcsec.
|
|
352
|
-
savefig (dict or str, optional):
|
|
352
|
+
savefig (dict or str, optional): Passed to ``close_figure``. Existing files may be overwritten, and the figure is closed after saving/showing. Defaults to None.
|
|
353
353
|
show (bool, optional): True means doing plt.show(). Defaults to False.
|
|
354
354
|
|
|
355
355
|
Returns:
|
|
@@ -384,16 +384,16 @@ def fftfits(fitsimage: str, center: str | None = None, lam: float = 1,
|
|
|
384
384
|
def findindex(u: np.ndarray | None = None, v: np.ndarray | None = None,
|
|
385
385
|
uobs: np.ndarray | None = None, vobs: np.ndarray | None = None
|
|
386
386
|
) -> np.ndarray:
|
|
387
|
-
"""Find
|
|
387
|
+
"""Find indices of the observed visibility points.
|
|
388
388
|
|
|
389
389
|
Args:
|
|
390
390
|
u (np.ndarray, optional): 1D array. The first frequency coordinate. Defaults to None.
|
|
391
|
-
v (np.ndarray, optional): 1D array. The second frequency
|
|
391
|
+
v (np.ndarray, optional): 1D array. The second frequency coordinate. Defaults to None.
|
|
392
392
|
uobs (np.ndarray, optional): 1D array. Observed u. Defaults to None.
|
|
393
393
|
vobs (np.ndarray, optional): 1D array. Observed v. Defaults to None.
|
|
394
394
|
|
|
395
395
|
Returns:
|
|
396
|
-
np.ndarray:
|
|
396
|
+
np.ndarray: Indices or an array of indices.
|
|
397
397
|
"""
|
|
398
398
|
if u is not None:
|
|
399
399
|
Nu, du = len(u), u[1] - u[0]
|
|
@@ -417,13 +417,13 @@ def fftfitssample(fitsimage: str, center: str | None = None,
|
|
|
417
417
|
getindex: bool = False,
|
|
418
418
|
u_sample: np.ndarray | None = None,
|
|
419
419
|
v_sample: np.ndarray | None = None) -> np.ndarray:
|
|
420
|
-
"""Find
|
|
420
|
+
"""Find indices or the visibilities on them from an image FITS file.
|
|
421
421
|
|
|
422
422
|
Args:
|
|
423
423
|
fitsimage (str): Input fits name in the unit of Jy/pixel.
|
|
424
424
|
center (str, optional): Text coordinate. Defaults to None.
|
|
425
|
-
index_u (np.ndarray, optional):
|
|
426
|
-
index_v (np.ndarray, optional):
|
|
425
|
+
index_u (np.ndarray, optional): Indices. Output from the getindex mode. Defaults to None.
|
|
426
|
+
index_v (np.ndarray, optional): Indices. Output from the getindex mode. Defaults to None.
|
|
427
427
|
xlim (list, optional): Range of x for zero padding in arcsec.
|
|
428
428
|
ylim (list, optional): Range of y for zero padding in arcsec.
|
|
429
429
|
getindex (bool, optional): True outputs [index_u, index_v]. Defaults to False.
|
|
@@ -431,7 +431,7 @@ def fftfitssample(fitsimage: str, center: str | None = None,
|
|
|
431
431
|
v_sample (np.ndarray, optional): 1D array. Observed u. Defaults to None.
|
|
432
432
|
|
|
433
433
|
Returns:
|
|
434
|
-
np.ndarray: Array of
|
|
434
|
+
np.ndarray: Array of indices or sampled FFT.
|
|
435
435
|
"""
|
|
436
436
|
F, u, v = fftfits(fitsimage=fitsimage, center=center, xlim=xlim, ylim=ylim)
|
|
437
437
|
if index_u is None or index_v is None:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from astropy import units, wcs
|
|
3
3
|
from astropy.io import fits
|
|
4
|
+
from typing import Any, Callable
|
|
4
5
|
|
|
5
6
|
from plotastrodata import const_utils as cu
|
|
6
7
|
from plotastrodata.coord_utils import coord2xy, xy2coord
|
|
@@ -9,15 +10,16 @@ from plotastrodata.noise_utils import estimate_rms
|
|
|
9
10
|
from plotastrodata.other_utils import isdeg, RGIxy, trim
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
def Jy2K(header
|
|
13
|
-
|
|
13
|
+
def Jy2K(header: Any = None, bmaj: float | None = None,
|
|
14
|
+
bmin: float | None = None,
|
|
15
|
+
restfreq: float | None = None) -> float | None:
|
|
14
16
|
"""Calculate a conversion factor in the unit of K/Jy.
|
|
15
17
|
|
|
16
18
|
Args:
|
|
17
19
|
header (optional): astropy.io.fits.open('a.fits')[0].header. Defaults to None.
|
|
18
|
-
bmaj (float, optional):
|
|
19
|
-
bmin (float, optional):
|
|
20
|
-
|
|
20
|
+
bmaj (float, optional): Beam major axis in arcsec. Defaults to None.
|
|
21
|
+
bmin (float, optional): Beam minor axis in arcsec. Defaults to None.
|
|
22
|
+
restfreq (float, optional): Rest frequency in Hz. Defaults to None.
|
|
21
23
|
|
|
22
24
|
Returns:
|
|
23
25
|
float: the conversion factor in the unit of K/Jy.
|
|
@@ -53,11 +55,11 @@ class FitsData:
|
|
|
53
55
|
Args:
|
|
54
56
|
fitsimage (str): Input FITS file name.
|
|
55
57
|
"""
|
|
56
|
-
def __init__(self, fitsimage: str):
|
|
58
|
+
def __init__(self, fitsimage: str) -> None:
|
|
57
59
|
self.fitsimage = fitsimage
|
|
58
60
|
|
|
59
61
|
def gen_hdu(self) -> None:
|
|
60
|
-
"""Generate self.hdu, which is astropy
|
|
62
|
+
"""Generate self.hdu, which is astropy.io.fits.open()[0].
|
|
61
63
|
"""
|
|
62
64
|
hdu = fits.open(self.fitsimage)
|
|
63
65
|
self.hdu = hdu[0]
|
|
@@ -75,7 +77,7 @@ class FitsData:
|
|
|
75
77
|
self.gen_hdu()
|
|
76
78
|
self.header = self.hdu.header
|
|
77
79
|
|
|
78
|
-
def get_header(self, key: str | None = None) ->
|
|
80
|
+
def get_header(self, key: str | None = None) -> Any:
|
|
79
81
|
"""Output the entire header or a value when a key is given.
|
|
80
82
|
|
|
81
83
|
Args:
|
|
@@ -94,7 +96,7 @@ class FitsData:
|
|
|
94
96
|
return None
|
|
95
97
|
|
|
96
98
|
def gen_beam(self, dist: float = 1.) -> None:
|
|
97
|
-
"""Generate
|
|
99
|
+
"""Generate self.bmaj, self.bmin, self.bpa from header['BMAJ'], etc.
|
|
98
100
|
|
|
99
101
|
Args:
|
|
100
102
|
dist (float, optional): bmaj and bmin are multiplied by dist. Defaults to 1..
|
|
@@ -146,7 +148,7 @@ class FitsData:
|
|
|
146
148
|
return a
|
|
147
149
|
|
|
148
150
|
def gen_data(self, Tb: bool = False, log: bool = False,
|
|
149
|
-
drop: bool = True, restfreq: float = None) -> None:
|
|
151
|
+
drop: bool = True, restfreq: float | None = None) -> None:
|
|
150
152
|
"""Generate data, which may be brightness temperature.
|
|
151
153
|
|
|
152
154
|
Args:
|
|
@@ -167,7 +169,7 @@ class FitsData:
|
|
|
167
169
|
d = np.log10(d.clip(np.min(d[d > 0]), None))
|
|
168
170
|
self.data = d
|
|
169
171
|
|
|
170
|
-
def get_data(self, **kwargs) -> np.ndarray:
|
|
172
|
+
def get_data(self, **kwargs: Any) -> np.ndarray:
|
|
171
173
|
"""Output data. This method can take the arguments of gen_data().
|
|
172
174
|
|
|
173
175
|
Returns:
|
|
@@ -177,7 +179,7 @@ class FitsData:
|
|
|
177
179
|
self.gen_data(**kwargs)
|
|
178
180
|
return self.data
|
|
179
181
|
|
|
180
|
-
def _read_cd(self):
|
|
182
|
+
def _read_cd(self) -> None:
|
|
181
183
|
h = self.header
|
|
182
184
|
cdij = ['CD1_1', 'CD1_2', 'CD2_1', 'CD2_2']
|
|
183
185
|
if not np.all([k in list(h.keys()) for k in cdij]):
|
|
@@ -211,7 +213,7 @@ class FitsData:
|
|
|
211
213
|
del h[k]
|
|
212
214
|
print(f'WCS rotation was found (CROTA2 = {crota2:f} deg).')
|
|
213
215
|
|
|
214
|
-
def _rotate_cd(self):
|
|
216
|
+
def _rotate_cd(self) -> None:
|
|
215
217
|
h = self.header
|
|
216
218
|
data = self.get_data()
|
|
217
219
|
ic = len(self.x) // 2
|
|
@@ -237,7 +239,10 @@ class FitsData:
|
|
|
237
239
|
self.data = datanew
|
|
238
240
|
print('Data values were interpolated for WCS rotation.')
|
|
239
241
|
|
|
240
|
-
def _get_genx_geny(self, center: str,
|
|
242
|
+
def _get_genx_geny(self, center: str,
|
|
243
|
+
dist: float
|
|
244
|
+
) -> tuple[Callable[[np.ndarray | None], None],
|
|
245
|
+
Callable[[np.ndarray | None], None]]:
|
|
241
246
|
h = self.header
|
|
242
247
|
cxy = (0, 0)
|
|
243
248
|
if center is not None and not self.wcsrot:
|
|
@@ -247,7 +252,7 @@ class FitsData:
|
|
|
247
252
|
cxy = coord2xy(center, coordorg)
|
|
248
253
|
slabel = ['x', 'y']
|
|
249
254
|
|
|
250
|
-
def wrapper(i: int):
|
|
255
|
+
def wrapper(i: int) -> Callable[[np.ndarray | None], None]:
|
|
251
256
|
def gen_s(s_in: np.ndarray | None) -> None:
|
|
252
257
|
if h.get(f'NAXIS{i+1}') is None or s_in is None:
|
|
253
258
|
s, ds = None, None
|
|
@@ -262,10 +267,11 @@ class FitsData:
|
|
|
262
267
|
|
|
263
268
|
return wrapper(0), wrapper(1)
|
|
264
269
|
|
|
265
|
-
def _get_genv(self, restfreq: float | None, vsys: float,
|
|
270
|
+
def _get_genv(self, restfreq: float | None, vsys: float,
|
|
271
|
+
pv: bool) -> Callable[[np.ndarray | None], None]:
|
|
266
272
|
h = self.header
|
|
267
273
|
|
|
268
|
-
def gen_v(v_in: np.ndarray) -> None:
|
|
274
|
+
def gen_v(v_in: np.ndarray | None) -> None:
|
|
269
275
|
vaxis = '2' if pv else '3'
|
|
270
276
|
if h.get(f'NAXIS{vaxis}') is None or v_in is None:
|
|
271
277
|
self.v, self.dv = None, None
|
|
@@ -299,7 +305,7 @@ class FitsData:
|
|
|
299
305
|
|
|
300
306
|
return gen_v
|
|
301
307
|
|
|
302
|
-
def _get_array(self, i: int) -> np.ndarray:
|
|
308
|
+
def _get_array(self, i: int) -> np.ndarray | None:
|
|
303
309
|
h = self.header
|
|
304
310
|
n = h.get(f'NAXIS{i:d}')
|
|
305
311
|
if n is None:
|
|
@@ -337,7 +343,9 @@ class FitsData:
|
|
|
337
343
|
if self.wcsrot:
|
|
338
344
|
self._rotate_cd()
|
|
339
345
|
|
|
340
|
-
def get_grid(self, **kwargs
|
|
346
|
+
def get_grid(self, **kwargs: Any
|
|
347
|
+
) -> tuple[np.ndarray | None, np.ndarray | None,
|
|
348
|
+
np.ndarray | None]:
|
|
341
349
|
"""Output the grids, [x, y, v]. This method can take the arguments of gen_grid().
|
|
342
350
|
|
|
343
351
|
Returns:
|
|
@@ -374,15 +382,16 @@ class FitsData:
|
|
|
374
382
|
def fits2data(fitsimage: str, Tb: bool = False, log: bool = False,
|
|
375
383
|
dist: float = 1., sigma: str | None = None,
|
|
376
384
|
restfreq: float | None = None, center: str | None = None,
|
|
377
|
-
vsys: float = 0., pv: bool = False, **kwargs
|
|
378
|
-
) -> tuple[np.ndarray, tuple[np.ndarray, np.ndarray
|
|
379
|
-
|
|
385
|
+
vsys: float = 0., pv: bool = False, **kwargs: Any
|
|
386
|
+
) -> tuple[np.ndarray, tuple[np.ndarray | None, np.ndarray | None,
|
|
387
|
+
np.ndarray | None],
|
|
388
|
+
np.ndarray, str | None, float | None]:
|
|
380
389
|
"""Extract data from a fits file. kwargs are arguments of FitsData.trim().
|
|
381
390
|
|
|
382
391
|
Args:
|
|
383
392
|
fitsimage (str): Input fits name.
|
|
384
|
-
Tb (bool, optional): True means
|
|
385
|
-
log (bool, optional): True means output data are
|
|
393
|
+
Tb (bool, optional): True means output data are brightness temperature. Defaults to False.
|
|
394
|
+
log (bool, optional): True means output data are logarithmic. Defaults to False.
|
|
386
395
|
dist (float, optional): Change x and y in arcsec to au. Defaults to 1..
|
|
387
396
|
sigma (str, optional): Noise level or method for measuring it. Defaults to None.
|
|
388
397
|
restfreq (float, optional): Used for velocity and brightness temperature. Defaults to None.
|
|
@@ -411,8 +420,8 @@ def data2fits(d: np.ndarray, h: dict = {},
|
|
|
411
420
|
Args:
|
|
412
421
|
d (np.ndarray): N-D array.
|
|
413
422
|
h (dict, optional): Additional FITS header. Defaults to {}.
|
|
414
|
-
templatefits (str, optional): FITS file whose header is used as a
|
|
415
|
-
fitsimage (str, optional): Output filename, with or without '.fits'. Defaults to 'test'.
|
|
423
|
+
templatefits (str, optional): FITS file whose header is used as a template. Defaults to None.
|
|
424
|
+
fitsimage (str, optional): Output filename, with or without '.fits'. Existing files with the same name are overwritten. Defaults to 'test'.
|
|
416
425
|
"""
|
|
417
426
|
_h = {} if templatefits is None else FitsData(templatefits).get_header()
|
|
418
427
|
_h.update(h)
|