fitscube 0.2.2__tar.gz → 0.3.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fitscube
3
- Version: 0.2.2
3
+ Version: 0.3.1
4
4
  Summary:
5
5
  Author: Thomson, Alec (S&A, Kensington)
6
6
  Author-email: AlecThomson@users.noreply.github.com
@@ -11,7 +11,8 @@ Classifier: Programming Language :: Python :: 3.8
11
11
  Classifier: Programming Language :: Python :: 3.9
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
- Requires-Dist: astropy (>=5.0,<6.0)
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Dist: astropy (>=5)
15
16
  Requires-Dist: numpy (>=1.20,<2.0)
16
17
  Requires-Dist: tqdm
17
18
  Description-Content-Type: text/markdown
@@ -46,18 +47,19 @@ pip install git+https://github.com/AlecThomson/fitscube.git
46
47
  Command line:
47
48
  ```bash
48
49
  fitscube -h
49
- # usage: fitscube [-h] [-o] [--freq-file FREQ_FILE | --freqs FREQS [FREQS ...] | --ignore-freq] file_list [file_list ...] out_cube
50
+ # usage: fitscube [-h] [-o] [--create-blanks] [--freq-file FREQ_FILE | --freqs FREQS [FREQS ...] | --ignore-freq] file_list [file_list ...] out_cube
50
51
 
51
- # Fitscube: Combine single-frequency FITS files into a cube. Assumes: - All files have the same WCS - All files have the same shape / pixel grid -
52
- # Frequency is either a WCS axis or in the REFFREQ header keyword - All the relevant information is in the first header of the first image
52
+ # Fitscube: Combine single-frequency FITS files into a cube. Assumes: - All files have the same WCS - All files have the same shape / pixel grid - Frequency is either a WCS
53
+ # axis or in the REFFREQ header keyword - All the relevant information is in the first header of the first image
53
54
 
54
55
  # positional arguments:
55
56
  # file_list List of FITS files to combine (in frequency order)
56
57
  # out_cube Output FITS file
57
58
 
58
- # optional arguments:
59
+ # options:
59
60
  # -h, --help show this help message and exit
60
61
  # -o, --overwrite Overwrite output file if it exists
62
+ # --create-blanks Try to create a blank cube with evenly spaced frequencies
61
63
  # --freq-file FREQ_FILE
62
64
  # File containing frequencies in Hz
63
65
  # --freqs FREQS [FREQS ...]
@@ -65,10 +67,10 @@ fitscube -h
65
67
  # --ignore-freq Ignore frequency information and just stack (probably not what you want)
66
68
 
67
69
  stokescube -h
68
- # usage: stokescube [-h] [-v STOKES_V_FILE] [--overwrite] stokes_I_file stokes_Q_file stokes_U_file output_file
70
+ # usage: stokescube [-h] [-v STOKES_V_FILE] [-o] stokes_I_file stokes_Q_file stokes_U_file output_file
69
71
 
70
- # Fitscube: Combine single-Stokes FITS files into a Stokes cube. Assumes: - All files have the same WCS - All files have the same shape / pixel
71
- # grid - All the relevant information is in the first header of the first image
72
+ # Fitscube: Combine single-Stokes FITS files into a Stokes cube. Assumes: - All files have the same WCS - All files have the same shape / pixel grid - All the relevant
73
+ # information is in the first header of the first image
72
74
 
73
75
  # positional arguments:
74
76
  # stokes_I_file Stokes I file
@@ -76,11 +78,11 @@ stokescube -h
76
78
  # stokes_U_file Stokes U file
77
79
  # output_file Output file
78
80
 
79
- # optional arguments:
81
+ # options:
80
82
  # -h, --help show this help message and exit
81
83
  # -v STOKES_V_FILE, --stokes_V_file STOKES_V_FILE
82
84
  # Stokes V file
83
- # --overwrite Overwrite output file if it exists
85
+ # -o, --overwrite Overwrite output file if it exists
84
86
  ```
85
87
 
86
88
  Python:
@@ -28,18 +28,19 @@ pip install git+https://github.com/AlecThomson/fitscube.git
28
28
  Command line:
29
29
  ```bash
30
30
  fitscube -h
31
- # usage: fitscube [-h] [-o] [--freq-file FREQ_FILE | --freqs FREQS [FREQS ...] | --ignore-freq] file_list [file_list ...] out_cube
31
+ # usage: fitscube [-h] [-o] [--create-blanks] [--freq-file FREQ_FILE | --freqs FREQS [FREQS ...] | --ignore-freq] file_list [file_list ...] out_cube
32
32
 
33
- # Fitscube: Combine single-frequency FITS files into a cube. Assumes: - All files have the same WCS - All files have the same shape / pixel grid -
34
- # Frequency is either a WCS axis or in the REFFREQ header keyword - All the relevant information is in the first header of the first image
33
+ # Fitscube: Combine single-frequency FITS files into a cube. Assumes: - All files have the same WCS - All files have the same shape / pixel grid - Frequency is either a WCS
34
+ # axis or in the REFFREQ header keyword - All the relevant information is in the first header of the first image
35
35
 
36
36
  # positional arguments:
37
37
  # file_list List of FITS files to combine (in frequency order)
38
38
  # out_cube Output FITS file
39
39
 
40
- # optional arguments:
40
+ # options:
41
41
  # -h, --help show this help message and exit
42
42
  # -o, --overwrite Overwrite output file if it exists
43
+ # --create-blanks Try to create a blank cube with evenly spaced frequencies
43
44
  # --freq-file FREQ_FILE
44
45
  # File containing frequencies in Hz
45
46
  # --freqs FREQS [FREQS ...]
@@ -47,10 +48,10 @@ fitscube -h
47
48
  # --ignore-freq Ignore frequency information and just stack (probably not what you want)
48
49
 
49
50
  stokescube -h
50
- # usage: stokescube [-h] [-v STOKES_V_FILE] [--overwrite] stokes_I_file stokes_Q_file stokes_U_file output_file
51
+ # usage: stokescube [-h] [-v STOKES_V_FILE] [-o] stokes_I_file stokes_Q_file stokes_U_file output_file
51
52
 
52
- # Fitscube: Combine single-Stokes FITS files into a Stokes cube. Assumes: - All files have the same WCS - All files have the same shape / pixel
53
- # grid - All the relevant information is in the first header of the first image
53
+ # Fitscube: Combine single-Stokes FITS files into a Stokes cube. Assumes: - All files have the same WCS - All files have the same shape / pixel grid - All the relevant
54
+ # information is in the first header of the first image
54
55
 
55
56
  # positional arguments:
56
57
  # stokes_I_file Stokes I file
@@ -58,11 +59,11 @@ stokescube -h
58
59
  # stokes_U_file Stokes U file
59
60
  # output_file Output file
60
61
 
61
- # optional arguments:
62
+ # options:
62
63
  # -h, --help show this help message and exit
63
64
  # -v STOKES_V_FILE, --stokes_V_file STOKES_V_FILE
64
65
  # Stokes V file
65
- # --overwrite Overwrite output file if it exists
66
+ # -o, --overwrite Overwrite output file if it exists
66
67
  ```
67
68
 
68
69
  Python:
@@ -0,0 +1,4 @@
1
+ from .fitscube import *
2
+ from .fitscube import combine_fits
3
+ from .stokescube import *
4
+ from .stokescube import combine_stokes
@@ -10,8 +10,7 @@ Assumes:
10
10
  """
11
11
 
12
12
  import os
13
- from collections import namedtuple
14
- from typing import List, Tuple, Union
13
+ from typing import List, NamedTuple, Optional, Tuple, Union
15
14
 
16
15
  import astropy.units as u
17
16
  import numpy as np
@@ -19,9 +18,88 @@ from astropy.io import fits
19
18
  from astropy.wcs import WCS
20
19
  from tqdm.auto import tqdm
21
20
 
22
- InitResult = namedtuple(
23
- "InitResult", ["data_cube", "header", "idx", "fits_idx", "is_2d"]
24
- )
21
+
22
+ class InitResult(NamedTuple):
23
+ data_cube: np.ndarray
24
+ """Output data cube"""
25
+ header: fits.Header
26
+ """Output header"""
27
+ idx: int
28
+ """Index of frequency axis"""
29
+ fits_idx: int
30
+ """FITS index of frequency axis"""
31
+ is_2d: bool
32
+ """Whether the input is 2D"""
33
+
34
+
35
+ def isin_close(element: np.ndarray, test_element: np.ndarray) -> np.ndarray:
36
+ """Check if element is in test_element, within a tolerance.
37
+
38
+ Args:
39
+ element (np.ndarray): Element to check
40
+ test_element (np.ndarray): Element to check against
41
+
42
+ Returns:
43
+ np.ndarray: Boolean array
44
+ """
45
+ return np.isclose(element[:, None], test_element).any(1)
46
+
47
+
48
+ def even_spacing(freqs: u.Quantity) -> Tuple[u.Quantity, np.ndarray]:
49
+ """Make the frequencies evenly spaced.
50
+
51
+ Args:
52
+ freqs (u.Quantity): Original frequencies
53
+
54
+ Returns:
55
+ Tuple[u.Quantity, np.ndarray]: Evenly spaced frequencies and missing channel indices
56
+ """
57
+ freqs_arr = freqs.value.astype(np.longdouble)
58
+ diffs = np.diff(freqs_arr)
59
+ min_diff = np.min(diffs)
60
+ # Create a new array with the minimum difference
61
+ new_freqs = np.arange(freqs_arr[0], freqs_arr[-1], min_diff)
62
+ missing_chan_idx = ~isin_close(new_freqs, freqs_arr)
63
+
64
+ return new_freqs * freqs.unit, missing_chan_idx
65
+
66
+
67
+ def create_blank_data(
68
+ data_cube: np.ndarray,
69
+ freqs: u.Quantity,
70
+ idx: int,
71
+ ) -> Tuple[Optional[np.ndarray], u.Quantity]:
72
+ """Create a new data cube with evenly spaced frequencies, and fill in the missing channels with NaNs.
73
+
74
+ Args:
75
+ data_cube (np.ndarray): Original data cube
76
+ freqs (u.Quantity): Original frequencies
77
+ idx: Index of frequency axis
78
+
79
+ Returns:
80
+ Tuple[Optional[np.ndarray], u.Quantity]: New data cube and frequencies
81
+ """
82
+ new_freqs, missing_chan_idx = even_spacing(freqs)
83
+ # Check if all frequencies present
84
+ all_there = isin_close(freqs, new_freqs).all()
85
+ if not all_there:
86
+ return None, new_freqs
87
+
88
+ # Create a new data cube with the new frequencies
89
+ new_shape = list(data_cube.shape)
90
+ new_shape[idx] = len(new_freqs)
91
+ new_data_cube = np.empty(new_shape) * np.nan
92
+ for old_chan, freq in enumerate(freqs):
93
+ new_chans = np.where(np.isclose(new_freqs, freq))[0]
94
+ assert len(new_chans) == 1, "Too many matching channels"
95
+ new_chan = new_chans[0]
96
+ new_slice = [slice(None)] * len(new_shape)
97
+ new_slice[idx] = new_chan
98
+ old_slice = [slice(None)] * len(new_shape)
99
+ old_slice[idx] = old_chan
100
+ new_data_cube[tuple(new_slice)] = data_cube[tuple(old_slice)]
101
+
102
+ return new_data_cube, new_freqs
25
103
 
26
104
 
27
105
  def init_cube(
@@ -131,11 +209,12 @@ def parse_freqs(
131
209
  return freqs
132
210
 
133
211
 
134
- def main(
212
+ def combine_fits(
135
213
  file_list: List[str],
136
214
  freq_file: Union[str, None] = None,
137
215
  freq_list: Union[List[float], None] = None,
138
216
  ignore_freq: bool = False,
217
+ create_blanks: bool = False,
139
218
  ) -> Tuple[fits.HDUList, u.Quantity]:
140
219
  """Combine FITS files into a cube.
141
220
 
@@ -197,6 +276,18 @@ def main(
197
276
  data_cube[tuple(slicer)] = plane
198
277
  # Write out cubes
199
278
  even_freq = np.diff(freqs).std() < 1e-6 * u.Hz
279
+ if not even_freq and create_blanks:
280
+ print("Trying to create a blank cube with evenly spaced frequencies")
281
+ new_data_cube, new_freqs = create_blank_data(
282
+ data_cube=data_cube,
283
+ freqs=freqs,
284
+ idx=idx,
285
+ )
286
+ if new_data_cube is not None:
287
+ even_freq = True
288
+ data_cube = new_data_cube
289
+ freqs = new_freqs
290
+
200
291
  if not even_freq:
201
292
  print("WARNING: Frequencies are not evenly spaced")
202
293
  print("Use the frequency file to specify the frequencies")
@@ -235,6 +326,11 @@ def cli():
235
326
  action="store_true",
236
327
  help="Overwrite output file if it exists",
237
328
  )
329
+ parser.add_argument(
330
+ "--create-blanks",
331
+ action="store_true",
332
+ help="Try to create a blank cube with evenly spaced frequencies",
333
+ )
238
334
  # Add options for specifying frequencies
239
335
  group = parser.add_mutually_exclusive_group()
240
336
  group.add_argument(
@@ -274,11 +370,12 @@ def cli():
274
370
  if overwrite:
275
371
  print("Overwriting output files")
276
372
 
277
- hdul, freqs = main(
373
+ hdul, freqs = combine_fits(
278
374
  file_list=args.file_list,
279
375
  freq_file=args.freq_file,
280
376
  freq_list=args.freqs,
281
377
  ignore_freq=args.ignore_freq,
378
+ create_blanks=args.create_blanks,
282
379
  )
283
380
 
284
381
  hdul.writeto(out_cube, overwrite=overwrite)
@@ -16,13 +16,12 @@ from astropy.io import fits
16
16
  from astropy.wcs import WCS
17
17
 
18
18
 
19
- def main(
19
+ def combine_stokes(
20
20
  stokes_I_file: str,
21
21
  stokes_Q_file: str,
22
22
  stokes_U_file: str,
23
23
  stokes_V_file: Union[str, None] = None,
24
24
  ) -> fits.HDUList:
25
-
26
25
  # Read in the data
27
26
  stokes_I = fits.getdata(stokes_I_file)
28
27
  stokes_Q = fits.getdata(stokes_Q_file)
@@ -115,7 +114,7 @@ def cli():
115
114
  f"Output file {output_file} already exists. Use --overwrite to overwrite."
116
115
  )
117
116
 
118
- hdul = main(
117
+ hdul = combine_stokes(
119
118
  stokes_I_file=args.stokes_I_file,
120
119
  stokes_Q_file=args.stokes_Q_file,
121
120
  stokes_U_file=args.stokes_U_file,
@@ -1,13 +1,13 @@
1
1
  [tool.poetry]
2
2
  name = "fitscube"
3
- version = "0.2.2"
3
+ version = "0.3.1"
4
4
  description = ""
5
5
  authors = ["Thomson, Alec (S&A, Kensington) <AlecThomson@users.noreply.github.com>"]
6
6
  readme = "README.md"
7
7
 
8
8
  [tool.poetry.dependencies]
9
9
  python = "^3.7"
10
- astropy = "^5.0"
10
+ astropy = ">=5"
11
11
  numpy = "^1.20"
12
12
  tqdm = "*"
13
13
 
@@ -1,4 +0,0 @@
1
- from .fitscube import *
2
- from .fitscube import main as combine_fits
3
- from .stokescube import *
4
- from .stokescube import main as combine_stokes
fitscube-0.2.2/setup.py DELETED
@@ -1,35 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from setuptools import setup
3
-
4
- packages = \
5
- ['fitscube']
6
-
7
- package_data = \
8
- {'': ['*']}
9
-
10
- install_requires = \
11
- ['astropy>=5.0,<6.0', 'numpy>=1.20,<2.0', 'tqdm']
12
-
13
- entry_points = \
14
- {'console_scripts': ['fitscube = fitscube.fitscube:cli',
15
- 'stokescube = fitscube.stokescube:cli']}
16
-
17
- setup_kwargs = {
18
- 'name': 'fitscube',
19
- 'version': '0.2.2',
20
- 'description': '',
21
- 'long_description': "# FITSCUBE\n\nFrom the [wsclean](https://wsclean.readthedocs.io/) docs:\n> WSClean does not output these images in a normal “imaging cube” like CASA does, i.e., a single fits file with several images in it. For now I’ve decided not to implement this (one of the reasons for this is that information about the synthesized beam is not properly stored in a multi-frequency fits file). One has of course the option to combine the output manually, e.g. with a simple Python script.\n\nThis is a simple Python script to combine (single-frequency or single-Stokes) FITS images manually.\n\nCurrent assumptions:\n- All files have the same WCS\n- All files have the same shape / pixel grid\n- Frequency is either a WCS axis or in the REFFREQ header keyword\n- All the relevant information is in the first header of the first image\n\n## Installation\n\nInstall from PyPI (stable):\n```\npip install fitscube\n```\n\nOr, onstall from this git repo (latest):\n```bash\npip install git+https://github.com/AlecThomson/fitscube.git\n```\n\n## Usage\n\nCommand line:\n```bash\nfitscube -h\n# usage: fitscube [-h] [-o] [--freq-file FREQ_FILE | --freqs FREQS [FREQS ...] | --ignore-freq] file_list [file_list ...] out_cube\n\n# Fitscube: Combine single-frequency FITS files into a cube. Assumes: - All files have the same WCS - All files have the same shape / pixel grid -\n# Frequency is either a WCS axis or in the REFFREQ header keyword - All the relevant information is in the first header of the first image\n\n# positional arguments:\n# file_list List of FITS files to combine (in frequency order)\n# out_cube Output FITS file\n\n# optional arguments:\n# -h, --help show this help message and exit\n# -o, --overwrite Overwrite output file if it exists\n# --freq-file FREQ_FILE\n# File containing frequencies in Hz\n# --freqs FREQS [FREQS ...]\n# List of frequencies in Hz\n# --ignore-freq Ignore frequency information and just stack (probably not what you want)\n\nstokescube -h\n# usage: stokescube [-h] [-v STOKES_V_FILE] [--overwrite] stokes_I_file stokes_Q_file stokes_U_file output_file\n\n# Fitscube: Combine single-Stokes FITS files into a Stokes cube. Assumes: - All files have the same WCS - All files have the same shape / pixel\n# grid - All the relevant information is in the first header of the first image\n\n# positional arguments:\n# stokes_I_file Stokes I file\n# stokes_Q_file Stokes Q file\n# stokes_U_file Stokes U file\n# output_file Output file\n\n# optional arguments:\n# -h, --help show this help message and exit\n# -v STOKES_V_FILE, --stokes_V_file STOKES_V_FILE\n# Stokes V file\n# --overwrite Overwrite output file if it exists\n```\n\nPython:\n```python\nfrom fitscube import combine_fits, combine_stokes\n\nhdu_list, frequencies = combine_fits(\n ['file1.fits', 'file2.fits', 'file3.fits'],\n)\nhdus_list = combine_stokes(\n 'stokes_I.fits',\n 'stokes_Q.fits',\n 'stokes_U.fits',\n)\n\n```\n\n## Convolving to a common resolution\nSee [RACS-Tools](https://github.com/AlecThomson/RACS-tools).\n\n## License\nMIT\n\n## Contributing\nContributions are welcome. Please open an issue or pull request.\n\n## TODO\n- [ ] Add support for non-frequency axes\n- [ ] Add tracking of the PSF in header / beamtable\n- [ ] Add convolution to a common resolution via RACS-Tools",
22
- 'author': 'Thomson, Alec (S&A, Kensington)',
23
- 'author_email': 'AlecThomson@users.noreply.github.com',
24
- 'maintainer': 'None',
25
- 'maintainer_email': 'None',
26
- 'url': 'None',
27
- 'packages': packages,
28
- 'package_data': package_data,
29
- 'install_requires': install_requires,
30
- 'entry_points': entry_points,
31
- 'python_requires': '>=3.7,<4.0',
32
- }
33
-
34
-
35
- setup(**setup_kwargs)
File without changes